CDROMDetection.c

/*
    File:       CDROMDetection.c
    
    Description:The sample shows how to determine whether a drive is a CD-ROM drive.
 
    Author:     BB
 
    Copyright:  Copyright: © 1998,1999 by Apple Computer, Inc.
                all rights reserved.
    
    Disclaimer: 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):
                6/23/99 Updated for Metrowerks Codwarrior Pro 2.1(KG)
 
*/
 
#include <TextUtils.h>
#include <LowMem.h>
#include <DriverGestalt.h>
#include <Files.h>
#include <Devices.h>
#include <SCSI.h>
 
#include <stdio.h>
#include <stdlib.h>
#include <SIOUX.h>      // Metrowerks only
 
// Be sure and put the #pragma options align=mac68k statement
// around any structure definitions which require a particular
// structure alignment; otherwise the compiler tries to word
// align the definitions, and nothing works on the PowerPC side.
#if PRAGMA_ALIGN_SUPPORTED
#pragma options align=mac68k
#endif
 
struct DeviceIdentParam {
    QElemPtr                        qLink;
    short                           qType;
    short                           ioTrap;
    Ptr                             ioCmdAddr;
    IOCompletionUPP                 ioCompletion;
    OSErr                           ioResult;
    StringPtr                       ioNamePtr;
    short                           ioVRefNum;
    short                           ioCRefNum;
    short                           csCode;
    DeviceIdent                     deviceIdentification;
    short                           csParam[9];
};
typedef struct DeviceIdentParam DeviceIdentParam, *DeviceIdentParamPtr;
 
#if PRAGMA_ALIGN_SUPPORTED
#pragma options align=reset
#endif
 
// This declares in C a type called "CallForEveryDriveQueueElement"
// which can then be declared as a variable in some other routine.
// That routine can initialize the variable to the address of some
// routine to call, and pass that as a parameter to CruiseDriveQueue.
typedef pascal OSErr (*CallForEveryDriveQueueElement)(DrvQElPtr dqPtr);
// to use the above declaration, you would have code such as this:
//      CallForEveryDriveQueueElement someCall = YourRoutineName;
//      CruiseDriveQueue(someCall);
// where "YourRoutineName" is replaced by the name of a routine
// which has been declared as follows:
//      pascal OSErr YourRoutineName(DrvQElPtr dqPtr);
//
// This is the declaration of the routine which cruises the drive
// queue, calling the input parameter (a procedure) with each drive 
// queue element in the drive queue.  It looks complicated, but
// it winds up being much easier to use.
pascal OSErr CruiseDriveQueue(CallForEveryDriveQueueElement theCall);
 
void            DetectCD(void);
OSErr           DeviceIdentification(
                    StringPtr drvrNamePtr, 
                    DrvQElPtr dqPtr, 
                    DeviceIdent *d);
pascal OSErr    PrintDriverInformation(DrvQElPtr dqPtr);
StringPtr       DrvrRefToName(short refNum);
OSErr           DetermineUsingDriverGestalt(
                    short driverRefNum,
                    short driveNumber,
                    OSType *deviceType);
 
//-----------------------------------------------------------------
// The main routine.  Start here.  We turn off the annoying
// "Do you want to save this window?" option of Metrowerks
// Standard Libraries
//
void main(void)
{
    SIOUXSettings.asktosaveonclose = false;     // Metrowerks only
    
    printf ("Sample showing how to detect CD-ROM drives\n\n");
 
    DetectCD();
}
 
//-----------------------------------------------------------------
// This is the declaration of the routine which cruises the drive
// queue, calling the input parameter (a procedure) with each drive
// queue element in the drive queue.  It looks complicated, but it
// winds up being easy to use.
//
pascal OSErr CruiseDriveQueue(CallForEveryDriveQueueElement theCall)
{
    register DrvQElPtr  dqPtr;
    OSErr               err;
 
    dqPtr = (DrvQElPtr) GetDrvQHdr()->qHead;
 
    while (dqPtr != NULL) 
    {
        err = theCall(dqPtr);
        if (err)
            break;
        dqPtr = (DrvQEl *) dqPtr->qLink;
    }
    return (err);
}
 
//-----------------------------------------------------------------
// This routine returns the device identification as documented 
// in Designing PCI Cards and Drivers
//
OSErr   DeviceIdentification( StringPtr drvrNamePtr, 
                              DrvQElPtr dqPtr, 
                              DeviceIdent *d)
{
    DeviceIdentParam    pb = {0};
    OSErr               err;
    
    pb.ioCompletion = nil;
    pb.ioNamePtr = drvrNamePtr;
    err = PBOpenSync((ParmBlkPtr)&pb);
 
    if (!err)
    {
        pb.ioVRefNum = dqPtr -> dQDrive;
        pb.ioCRefNum = dqPtr -> dQRefNum;
        pb.csCode = 120;
        err = PBStatusSync((ParmBlkPtr)&pb);
    }
    *d = pb.deviceIdentification;
    return err;
}
 
//-----------------------------------------------------------------
// This routine just sets up a variable with the name of a routine
// to call for every drive queue element, and then calls our
// generic "scan the drive queue" routine.  This will call the
// routine we passed in for every drive queue element.
// We tell the "CruiseDriveQueue" routine to call our 
// PrintDriverInformation routine for each entry in the drive queue.
void DetectCD(void)
{
    CallForEveryDriveQueueElement someCall = PrintDriverInformation;
    CruiseDriveQueue(someCall);
}
 
//-----------------------------------------------------------------
// PrintDriverInformation prints information about the driver for
// each Drive Queue Element we pass it.  If the drive in question
// passes tests to see if it's a CD-ROM drive, we print additional
// information
//
pascal OSErr PrintDriverInformation(DrvQElPtr dqPtr)
{
    StringPtr   drvrNamePtr;
    OSType      theDriveType;
    DeviceIdent d;
    OSErr       err;
    
    // dqPtr->dQRefNum contains the driver reference number.  
    // In order to display something to the user, we will get
    // the driver name by looking in the unit table (an array)
    // to find the driver name.
    drvrNamePtr = DrvrRefToName(dqPtr->dQRefNum);
 
    printf("DriverName: %.*s ", drvrNamePtr[0], &drvrNamePtr[1]);
    if ( EqualString(drvrNamePtr, "\p.AppleCD", false, true) )
        printf("(This is a CD-ROM drive.) ");
    
    err = DetermineUsingDriverGestalt(dqPtr->dQRefNum, dqPtr->dQDrive, &theDriveType);
    if (!err)
    {
        char c[5];
        BlockMoveData(&theDriveType, c, 4);
        c[4] = 0;
        printf("Driver Gestalt returned '%s'", c);
    }
    
    err = DeviceIdentification(drvrNamePtr, dqPtr, &d);
    if (!err)
    {
        printf("\nDevice reports that it is bus type %d, bus %d, target %d, partition/LUN %d.",
            d.diReserved, d.bus, d.targetID, d.LUN);
    }
    printf("\n");
    return noErr;
}
 
//-----------------------------------------------------------------
// Lookup driver name from the unit table. The driver name starts
// at 18 bytes into the driver itself.  From the Unit Table entry
// for this driver, get the name, but take into account whether it
// is a handle based driver or a pointer based driver.
//
StringPtr DrvrRefToName(short refNum)
{
    AuxDCEHandle*       UTable  = (AuxDCEHandle*) LMGetUTableBase();
    DCtlPtr             dctl;
    Ptr                 p;
    static StringPtr    driverName;
    
    if ( refNum == 0 )
    {
        driverName = "\p<none>";
    }
    else
    {
        dctl = (DCtlPtr) *UTable[~refNum];
        p    =  dctl->dCtlDriver;
        
        // a RAMbased driver is handle based, a ROMbased driver
        // is pointer based.  If it's a handle based driver, we
        // need one more level of indirection.  The following 
        // test provides that indirection if necessary.
        if( dctl->dCtlFlags  & dRAMBasedMask) 
            p = (void*) *p;
 
        if ( p != nil )
            driverName = (void *)(p+18);
        else
            driverName = "\p-Purged-";
    }
    return  ( driverName);
}
 
//-----------------------------------------------------------------
// Call the driver using the DriverGestalt status code.  The result
// tells us if this driver is a CD-ROM
//
OSErr DetermineUsingDriverGestalt(short driverRefNum, 
                                  short driveNumber, 
                                  OSType *deviceType)
{
    DriverGestaltParam  pb;
    OSErr               err;
    
    pb.ioVRefNum            = driveNumber;
    pb.ioCRefNum            = driverRefNum;
    pb.csCode               = kDriverGestaltCode;
    pb.driverGestaltSelector = kdgDeviceType;
    
    err = PBStatusSync((ParmBlkPtr) &pb);
    
    if (!err)
    {
        *deviceType = pb.driverGestaltResponse;
    }
    return (err);
}