Documentation Archive Developer
Search

ADC Home > Reference Library > Technical Notes > Legacy Documents > Hardware & Drivers >

Legacy Documentclose button

Important: This document is part of the Legacy section of the ADC Reference Library. This information should not be used for new development.

Current information on this Reference Library topic can be found here:

Device Manager Q&As

CONTENTS

This Technical Note contains a collection of archived Q&As relating to a specific topic--questions sent the Developer Support Center (DSC) along with answers from the DSC engineers. Current Q&As can be found on the Macintosh Technical Q&As web site.

[Oct 01 1990]






System 7.0 and SetChooserAlert bug

Date Written: 12/28/92

Last reviewed: 3/1/93

With System 7.0, SetChooserAlert doesn't seem to work as documented. It does change the state of bit 6 in HiliteMode, but the "Please change your page setup" message comes up in the Chooser regardless. Is there any workaround for this?

___

The problem you reported is indeed a bug with system software version 7.0 and later. Correcting the system software is the only solution, though this fix may be as simple as a tweak in a future Chooser release (which could come out sooner than a new system software release). Until such a fix is made, SetChooserAlert will only work with system software prior to version 7.0.

Back to top

PBRead/PBWrite instead of FSRead/FSWrite after asynchronous call

Date Written: 11/24/92

Last reviewed: 6/14/93

I want to send an asynchronous Control command to my driver and while the command is being processed continue work. However, the continued work includes sending FSWrite and FSRead commands to the driver. Is this possible?

___

When you send an asynchronous call to a driver, it gets added to the driver I/O queue. The driver deals with each request in a sequential order. Both FSWrite and FSRead are synchronous. This means that the I/O request is added to the driver queue, and then the Device Manager loops in an internal routine named SyncWait, waiting for ioResult to be less than or equal to 0 (noErr). We assume that you have an interrupt service routine or some other method of completing the I/O request.

If you make a call to either FSWrite or FSRead after doing an asynchronous call, you set up a race condition where the driver is waiting to complete the asynchronous Control call, while the FSRead/Write call is waiting for its completion flag to be marked as done. This won't work.

The solution is to use PBWrite and PBRead instead, and set the asynchronous flag in the call. This way, the driver can properly queue up the calls and handle them in the order issued.

Note that the calls are queued up in a first-in-first-out order. Your control call will complete before your PBRead or PBWrite starts (that is, the Macintosh isn't truly asynchronous in this respect). For instance, if you did the following:

PBControl, PBWrite, PBRead, PBRead

they'd be executed in precisely that fashion. The last PBRead won't be executed until all three previous calls have finished.

For more information about the way drivers work, you should look on the latest Developer CD Series disc for a preliminary copy of the new Inside Macintosh chapter on devices. The path to this volume is: Technical Documentation: Inside Macintosh: Devices The new chapter is far better than the old Inside Macintosh chapters at explaining how device drivers work. This chapter also has a pretty good explanation of how asynchronous and immediate calls work.

Back to top

Ensuring that Macintosh driver isn't open already

Date Written: 11/13/92

Last reviewed: 3/1/93

How do I ensure that my driver isn't open already?

___

Check the dOpened flag in your driver's dCtlEntry. This bit is set automatically by the Device Manager when your driver is opened, then cleared when it's closed. However, most drivers should be written so they can take superfluous open calls; many programmers use Open as a method of getting a driver's refNum and opening it at the same time; your driver should check to see if it's already been opened, and if so, do nothing.

Back to top

Determining if a file is read from CD-ROM or hard disk

Date Written: 8/23/91

Last reviewed: 9/24/91

How can we tell whether a particular Macintosh file is being read from a CD-ROM or a hard disk?

___

You can call the Device Manager routine OpenDriver using the ".AppleCD" string to find a driver reference number of the AppleCD SC drive. If there is more than one AppleCD SC drive hooked up, then additional ".AppleCD" driver reference numbers can be obtained by using the PBControl call with a csCode = 97 (WhoIsThere). This command returns a mask of which SCSI devices are being serviced by the ".AppleCD" driver (that is, which other drives are AppleCD SCs).

The following code returns the driver reference number for an ".AppleCD" driver instance. The input parameter CDDrive specifies which logical AppleCD SC drive in the SCSI chain to open.

#define csWhoIsThere    97

typedef unsigned short    Word;
typedef    unsigned long    Long;

typedef struct WhoIsThereRec {
    ParamBlockHeader
    short        ioRefNum;
    short        csCode;
    struct {
        Byte    fill;
        Byte    SCSIMask;
    } csParam;
} WhoIsThereRec;

pascal    OSErr OpenCD(Byte CDDrive, short *ioRefNum) {

    auto    OSErr            osErr;
    auto    short            ioRefNumTemp,
                            CDDriveCount,
                            SCSIID;
    auto    WhoIsThereRec    *pb;

    pb = (WhoIsThereRec *) NewPtrClear(sizeof (*pb));
    osErr = MemError();
    if (0 != pb && noErr == osErr) {
        osErr = OpenDriver("\p.AppleCD", &ioRefNumTemp);
        if (noErr == osErr) {
            (*pb).ioRefNum        = ioRefNumTemp;
            (*pb).csCode        = csWhoIsThere;
            osErr = PBStatus((ParmBlkPtr)pb, false);
            if (noErr == osErr) {
                CDDriveCount = 0;
                for (SCSIID = 0; SCSIID < 7; ++SCSIID) {
                    if (BitTst(&(*pb).csParam.SCSIMask, 7 - SCSIID)) {
                        ++CDDriveCount;
                        if (CDDrive == CDDriveCount) {
                            *ioRefNum = -(32 + SCSIID) - 1;
                            DisposPtr((Ptr) pb);
                            return noErr;
                        }
                    }
                }
                osErr = paramErr;
            }
        }
        DisposPtr((Ptr) pb);
    }
    return osErr;
}

You can modify OpenCD to do exactly what it is you need it to do. Or, you might use it to iterate over the logical CD drive numbers from 0 to 6 until OpenCD returns something other than noErr. If you modify the OpenCD routine, you'll need the AppleCD SC Developers Guide, Revised Edition to be successful. Note also that all unused fields of a parameter block used with the ".AppleCD" driver must be set to zero before calling PBControl.

Iterating over the seven possibilities will result in a table of known ".AppleCD" drive entries - perhaps something like the following structure:

    struct {
     int count;
     short cdDRefNum[7];
    } knownCDDRefNum;

With this table in hand, you can match the driver reference number for the volume of any file. In most cases you know which volume the file was on when you opened it. Also, the routines PBGetFCBInfo and PBGetCatInfo both return the volume reference number for a file.

Once you determine the volume reference numbers, either traverse the VCB table or call PBGetVInfo to get the driver reference number. This driver reference number is the driver handling all requests made by the File System for your file!

If that driver reference number for the file is in our "knownCDDRefNum" table, then that file resides on an AppleCD SC drive. This technique works only for Apple or Apple-compatible drivers. An alternate approach to the problem is stepping through the driver table and locating all entries with the name ".AppleCD," noting their driver reference numbers, and then following the procedure outlined above to determine if a file is on a volume owned by that driver.

The csCode method is far clearer than this one, and should be used whenever possible. The bottom line is that there is no guaranteed method of locating a CD-ROM drive due to the lack of a standardized driver model.

X-Ref:

Device Manager chapters of Inside Macintosh Volumes II, IV, and V

"Finding a Slot for a Driver" note on latest Developer CD Series disc

Back to top

Accessing a Macintosh driver resource fork at accRun time

Date Written: 6/20/91

Last reviewed: 8/13/91

How do I get Macintosh resources from a driver Init file at accRun time? I can't think of a way other that getting the full path name, which is discouraged.

___

Basically, you need to call PBGetFCBInfo at INIT time to grab the filename, directory ID and volume reference number for the INIT's resource fork. You can then store these in your INIT and pull them out at accRun time. At accRun, just call HOpenResFile to get to your resources. This method is _much_ better than using a full pathname, since this still works in the case where the user re-names the folder containing your INIT.

Here's some sample code that does what you need:

OSErr GetCurResLocn(short *saveVRefNum,long *saveDirID,StringPtr saveFName)
{
    FCBPBRec pb;
    OSErr err;
    short theFile;
    Str255 fName;

    theFile = CurResFile();

    pb.ioFCBIndx = 0;
    pb.ioVRefNum = 0;
    pb.ioRefNum = theFile;
    pb.ioNamePtr = saveFName;

    err = PBGetFCBInfo (&pb,false);

    *saveVRefNum = pb.ioFCBVRefNum;
    *saveDirID = pb.ioFCBParID;

    return err;
}

OSErr SetCurResLocn(short saveVRefNum,long saveDirID,StringPtr saveFName,
                    short *newResFile)
{
    short resRef;
    OSErr err;

    HOpenResFile(saveVRefNum,saveDirID,saveFName,fsRdWrPerm);
    err = ResError();
    if (err!=noErr)
        return err;

    UseResFile(resRef); /* <-- needed in case the res. file was prev. open */

    *newResFile = resRef;

    return ResError();
}

void main()
{
    OSErr err;
    short saveVRefNum;
    long saveDirID;
    Str255 saveFName;
    short newResFile;

    err = GetCurResLocn(&saveVRefNum,&saveDirID,saveFName);
    if (err!=noErr)
        return;

    /* ... pass control off to computer here (we're an app, so we fake it) ... */

    err = SetCurResLocn(saveVRefNum,saveDirID,saveFName,&newResFile);
    if (err!=noErr)
        DebugStr("\pfailed");
}

As you can see, this is an application, so you'll have to do some minor modifications (possibly convert to 680x0). It's pretty straightforward, and the HOpenResFile call is included in MPW glue for MPW 3.0 and is a built-in call for System 7.

Back to top

DAs in background under System 7.0 lack UnitTable entries

Date Written: 3/14/91

Last reviewed: 6/17/91

Under System 6 my driver, which runs all the time, can send a control call to my open DA (because it too is a driver). Under System 7.0 I get badUnitErr errors (-21) because evidently my DA resides in a different process that is inaccessible to my system-resident driver. How can I get around this?

___

DAs in System 7.0 do not actually have UnitEntries unless they are currently running, so your driver cannot call your DA unless the DA is frontmost. What you might consider instead is having the DA periodically issue a call to the driver, asking if there is anything for it at the moment. If you have an entity that hands data to your resident driver, and the DA then requests the data from the resident driver from time to time, you should have a very robust mechanism, albeit a slightly slower one with greater latency.

Back to top

JMP or JSR When Calling IODone

Date Written: 12/12/90

Last reviewed: 6/14/93

After an I/O call to a Macintosh slot device driver, shouldn't the IODone routine be called by a JSR instead of a JMP instruction in order for a slot device to return to it with D0 set to an appropriate value depending on whether the interrupt was serviced?

___

Correct. You call IODone when the queued I/O request has been fully completed. If the interrupt handler is completing an asynchronous call, you need to call IODone. IODone returns via an RTS, so the sequence would be something like:

  1. Program runs
  2. The interrupt occurs
  3. Primary interrupt handler code JSRs to secondary (such as VIA) interrupt handler
  4. Secondary interrupt handler BSRs to slot handler code
  5. Slot handler does a JSR to your Interrupt Service Routine (ISR)
  6. The I/O request is complete, so your ISR sets result code in D0 and JSRs to IODone
  7. IODone does an RTS, which returns to your ISR
  8. Your ISR sets D0 to non-zero and does an RTS to slot handler
  9. Slot handler does an RTS back to secondary handler
  10. Secondary handler RTSes back to primary handler
  11. Primary interrupt handler does an RTE back to the program
  12. The program continues

If you're just handling a hardware interrupt or the I/O isn't yet complete, don't call IODone. Do an RTS as in the source code example in Chapter 9 of Designing Cards and Drivers for the Macintosh Family (starting with the BeginIH label).

Back to top

New info on Macintosh Device Manager calls

Date Written: 12/5/90

Last reviewed: 6/14/93

Are Macintosh Device Manager status calls with csCode=1 calls filtered out? Also, are all high-level Device Manager routines always executed synchronously? If all the calls are synchronous, why is there a high level KillIO routine (to terminate current and pending processes)?

___

Yes, a Status call made with a csCode of 1 never calls your driver. Instead, it returns (in the csParam field) the handle to your driver's Device Control Entry from the Unit Table.

High-level Device Manager calls are executed synchronously. Only the low-level calls can be specified to execute asynchronously. The high-level KillIO routine is useful for terminating I/O pending from a low-level call, which may have been initiated by someone else.

X-Refs:

Inside Macintosh Volume II, Chapter 6

Back to top

Given a Macintosh gdRefNum, how can I find the associated slot?

Date Written: 3/9/90

Last reviewed: 12/17/90

Given a Macintosh gdRefNum, how can I find the associated slot?

___

Get the slot number from the auxiliary DCE. The following code snippet indexes through the GDevices (it's assumed the check showed the presence of Color QuickDraw), and pulls the slot number from each GDevice record:

  gGDHandle := GetDeviceList; {get the first GDevice list handle}
  repeat
    if gGDHandle <> nil then
      begin
        gAuxDCEHandle := AuxDCEHandle(GetDCtlEntry(gGDHandle^^.gdRefNum));
        { do whatever slot specific work is desired, now that the slot }
        { number is known }
        gGDHandle := GetNextDevice(gGDHandle);
        { pass in present GDHandle; the next one is returned }
      end;
    until gGDHandle = nil;

X-Refs:

"Device Manager," Inside Macintosh Volumes II and IV

"Graphics Devices," Inside Macintosh Volume V

Back to top

Macintosh journaling mechanism

Date Written: 5/3/89

Last reviewed: 12/17/90

How can I use the journaling mechanism described in Inside Macintosh?

___

The old journaling mechanism isn't supported any more. It is no longer necessary because of MacroMaker and similar products. MacroMaker now "owns" the older driver, and any future journaling will be done through MacroMaker. Currently there is no technical documentation available for MacroMaker. The current abilities of MacroMaker may not support what some developers will want to do with journaling. Future versions of MacroMaker may add more features.

Back to top

How do I support locked and ejectable SCSI devices?

Date Written: 5/14/90

Last reviewed: 6/14/93

How do I support locked and ejectable SCSI devices?

___

The only things you should have to support are the _DriveStatus call and modify the DrvQEl record. The rest will be handled by the Macintosh system. The _DriveStatus call gives the information for srvStsCode. This is how the system will know what your disk can support. It is typically only used by floppy (or removable) disk drives. The sample SCSI driver from Apple doesn't need to support it, because it doesn't support any of the information in the _DriveStatus call.

The DrvSts record contains some information of no concern here. The diskInPlace, twoSideFmt, and needsFlush are probably ignored for your device. It's best to zero them out. The DrvSts information is pretty much just the same information returned in the DrvQEl record.

Also, people sometimes get into trouble while developing a driver because current File Manager documentation about the drive queue element is vague. There are 4 bytes in front of a DrvQEl record. These determine the device's abilities, but these bytes are not allocated by the system. When creating the DrvQEl record, you need to add these four bytes in front of the record yourself. The pointer to a DrvQEl will be the actual record, which is after these 4 bytes. To read these bytes yourself, you'll have to subtract 4 bytes from the DrvQEl pointer.

It is important to note that the Disk Switch error dialog is not an actual dialog, but the system error. It is handled by the same code as SysError which shows the system bomb alert. While this window is present, _SystemTask is not being called. This means the driver will not get an accRun call. To work around this, you will need a VBL task. When the VBL is called it checks the SCSI bus for being free and if so, tests for a new cartridge. Once found, posts a diskInsertEvt. This will be received by the driver.

While the media is inserted, the VBL should not be running until after the cartridge is ejected. Otherwise, the SCSI bus will continue to be accessed unnecessarily, which slows the bus. The VBL task could also slow the system while virtual memory is running.

Back to top

Downloadables

Acrobat gif

Acrobat version of this Note (76K)

Download


Back to top