Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
INIT.c
/*------------------------------------------------------------------------------ |
* |
* Macintosh Developer Technical Support |
* |
* Sample Control Panel Device and INIT Combination |
* |
* Program: INIT - CDEV |
* File: INIT.c - C Source |
* |
* Copyright © 1990 Apple Computer, Inc. |
* All rights reserved. |
* |
*-----------------------------------------------------------------------------*/ |
#include <Common.h> |
#include <SAGlobals.h> |
#include <Processes.h> |
/*------------------------------------------------------------------------------ |
Global variables |
------------------------------------------------------------------------------*/ |
CommonGlobalsRec gCommonGlobals; /* Globals common to INIT and CDEV */ |
SessionRecord gSessionRecord; /* Data for PPC communications */ |
long gTimeLastBeeped; /* Tickcount when we last beeped */ |
/*------------------------------------------------------------------------------ |
Procedure Prototypes and External Routines |
------------------------------------------------------------------------------*/ |
OSErr c_Install(void); |
OSErr DoPPCInit(void); |
OSErr ReadPreferences(void); |
void c_SystemTask(void); |
OSErr OpenAPort(void); |
pascal void InformCompProc(PPCParamBlockPtr ppb); |
pascal void WriteCompProc(PPCParamBlockPtr ppb); |
pascal void EndCompProc(PPCParamBlockPtr ppb); |
extern void a_SetA5Ref(A5RefType); |
extern A5RefType a_GetA5Ref(void); |
/*------------------------------------------------------------------------------ |
OSErr c_Install(void) |
This is the C portion of the installation process. It is called from the |
assembly installation code at INIT time. |
We're going to be making all of our memory allocation from the System |
zone. There's really no other choice. We're an INIT; we don't have our own |
zone to get memory from. So we save off whatever the current zone is |
(which very well may be the System Zone -- what do I know?), and set the |
zone to the System Zone. |
Next, we get the memory for our globals. We call MakeA5World to do this, |
which is a modification of the routine described in Technote #256. If we |
don't get our memory, restore the zone and return to our caller with an |
"out of memory" error. If we do get the memory, then save off the old A5 |
and set A5 to be our A5. Also, save our A5 reference so that when our |
SystemTask patch runs, it knows what value to use for A5. |
After that, we do some more initialization specific to the function of |
this INIT (which is to beep, annoying the **** out of the user). We call |
two subroutines for this, which do PPCToolbox setup, and read our |
preferences file. |
Finally, we clean up and leave. If any errors occured, we gett rid of the |
buffer for our globals. We then restore A5 and the zone, and return any |
error numbers. |
------------------------------------------------------------------------------*/ |
OSErr c_Install(void) |
{ |
OSErr err; /* For any routines that return errors */ |
THz oldZone; /* Holds the current zone while we switch |
over to the System zone for our memory |
allocation. */ |
A5RefType A5Ref; /* The reference to our globals. This is |
created by the SAGlobals package. */ |
long oldA5; /* Holds the current A5 when we switch |
over to the A5 for our own globals. */ |
err = noErr; |
oldZone = GetZone(); |
SetZone(SystemZone()); |
MakeA5World(&A5Ref); |
if (A5Ref == nil) { |
SetZone(oldZone); |
return(memFullErr); |
} |
a_SetA5Ref(A5Ref); |
oldA5 = SetA5World(A5Ref); |
(void) DoPPCInit(); |
(void) ReadPreferences(); |
(void) SetA5(oldA5); |
SetZone(oldZone); |
return (err); |
} |
/*------------------------------------------------------------------------------ |
OSErr DoPPCInit(void) |
Check to see if we have PPCToolbox facilities. If so, initialize the |
PPCToolbox, open a port, and perform a PPCInform call on the port (this is |
done with the EndCompProc procedure down below). |
If there are any errors, we set "err" to the result code. However, we have |
to distinguish between hard and soft errors. If the PPCToolbox is not |
around, that is a soft error, so we make sure to clear "err" if Gestalt |
says PPCToolbox is not there. Likewise, if the PPCInform call reports a |
result of "1", that means the asynchronous call is pending, and is also a |
soft error, so we also clear "err" in that case. Otherwise, any errors are |
hard errors, and indicate some serious problems. In that case, we report |
the error to our caller so that it can abort the installation of the INIT. |
------------------------------------------------------------------------------*/ |
OSErr DoPPCInit(void) |
{ |
OSErr err; /* For any routines that return errors */ |
long gestaltResult; /* Gestalt result. duh! */ |
err = Gestalt(gestaltPPCToolboxAttr, &gestaltResult); |
if (!err) { |
err = PPCInit(); |
if (!err) { |
err = OpenAPort(); |
if (!err) { |
EndCompProc(&gSessionRecord.pb); |
err = gSessionRecord.pb.informParam.ioResult; |
if (err > 0) { |
err = noErr; /* cover up for soft errors */ |
} |
} |
} |
} else |
err = noErr; /* cover up for soft errors */ |
return (err); |
} |
/*------------------------------------------------------------------------------ |
OSErr ReadPreferences(void) |
See if we can read the preferences file. First, we see if the FindFolder |
routine is present with a call to Gestalt. If so, we look for the |
preferences file. If one exists, we open it up, and read the values into |
our public globals buffer (the preferences file is just an image of that |
buffer, so the values read right in). |
If the FindFolder routine doesn't exist on this machine, or the |
preferences file doesn't exists, or there was an error when reading the |
file, we go right ahead and use a set of hard-wired values. All errors at |
this stage of the INIT are considered soft errors, so we clear "err". |
Finally, we set "gTimeLastBeeped" to be the current tickcount. |
------------------------------------------------------------------------------*/ |
OSErr ReadPreferences(void) |
{ |
OSErr err; /* For any routines that return errors */ |
long gestaltResult; /* Gestalt result. duh! */ |
Boolean useDefaults; /* Boolean that we use to determine if we |
were able to read the preferences file. |
If not, this is TRUE, and we use default |
values instead. */ |
FSSpec spec; /* FSSpec for reading our preferences */ |
short refNum; /* Refnum for our preferences file */ |
long amountToRead; /* Number of bytes to read from pref. */ |
useDefaults = true; |
err = Gestalt(gestaltFindFolderAttr, &gestaltResult); |
if ((err == noErr) && (gestaltResult != 0)) { |
err = FindFolder(kOnSystemDisk, kPreferencesFolderType, |
kCreateFolder, &spec.vRefNum, &spec.parID); |
if (!err) { |
(void) PLstrcpy(spec.name, kPrefsFileName); |
err = FSpOpenDF(&spec, fsRdPerm, &refNum); |
if (!err) { |
amountToRead = sizeof(gCommonGlobals); |
err = FSRead(refNum, &amountToRead, (Ptr) &gCommonGlobals); |
if (!err) { |
err = FSClose(refNum); |
useDefaults = false; |
} |
} |
} |
} |
err = noErr; |
if (useDefaults) { |
gCommonGlobals.timesToBeep = 3; |
gCommonGlobals.beepInterval = 60*60; /* start off once a min. */ |
} |
gTimeLastBeeped = TickCount(); |
return(err); |
} |
/*------------------------------------------------------------------------------ |
void c_SystemTask(void) |
This routine is the meat of the SystemTask() patch. It is called from |
a_SystemTask, an assembly routine that saves the registers, calls us, |
restores the registers, and then calls the real SystemTask() in a way that |
doesn't constitute a tail patch. |
All we do here is see if an appropriate amount of time has passed. If so, |
we beep a certain number of times. Otherwise, we do nothing. |
------------------------------------------------------------------------------*/ |
void c_SystemTask(void) |
{ |
long oldA5; |
short loopy; |
oldA5 = SetA5World(a_GetA5Ref()); |
if (TickCount() >= gTimeLastBeeped + gCommonGlobals.beepInterval) { |
for (loopy = 0; loopy < gCommonGlobals.timesToBeep; ++loopy) { |
SysBeep(5); |
} |
gTimeLastBeeped = TickCount(); |
} |
(void) SetA5(oldA5); |
} |
/*------------------------------------------------------------------------------ |
OSErr OpenAPort(void) |
Used to open a PPC port. We identify ourselves using the portName record. |
First, we give ourselves a name that will show up in the PPCBrowser |
(portName.name). We then give ourselves an identify to other processes. We |
can do this either by name or by creator/type signatures. In our case, we |
choose creator/type ('INCD'/'INIT'). |
We then set up for a PPCOpen call. We are making a synchronous call, so we |
don't need a completion routine. ServiceType and resFlag are set to |
required values (per Inside Mac). We make the INIT not visible over the |
network. However, if we did, it's possible to talk to it from another |
Macintosh. If the other Mac know our communication protocol, it could |
change our beep parameters out from under us. Next, we point to the name |
record we want to use for our port, and use the default location name |
(which identifies our computer to other computers on the network). |
Finally, we make the PPCOpen call synchronously, and return any errors. |
------------------------------------------------------------------------------*/ |
OSErr OpenAPort(void) |
{ |
gSessionRecord.portName.nameScript = GetScriptManagerVariable(smSysScript); |
(void) PLstrcpy(gSessionRecord.portName.name, "\pBG Beeper"); |
gSessionRecord.portName.portKindSelector = ppcByCreatorAndType; |
#if GENERATING68K |
// Universal Interfaces 2.0 |
gSessionRecord.portName.u.port.portCreator = kCreator; |
gSessionRecord.portName.u.port.portType = 'INIT'; |
#else |
gSessionRecord.portName.u.port.creator = kCreator; |
gSessionRecord.portName.u.port.type = 'INIT'; |
#endif |
gSessionRecord.pb.openParam.ioCompletion = nil; |
gSessionRecord.pb.openParam.serviceType = ppcServiceRealTime; |
gSessionRecord.pb.openParam.resFlag = 0; |
gSessionRecord.pb.openParam.networkVisible = false; |
gSessionRecord.pb.openParam.portName = &gSessionRecord.portName; |
gSessionRecord.pb.openParam.locationName = nil; // use the default location |
return(PPCOpen(&gSessionRecord.pb.openParam, false)); |
} |
/*------------------------------------------------------------------------------ |
pascal void InformCompProc(PPCParamBlockPtr ppb) |
After we open a PPC port, we make an asynchronous PPCInform call on it. |
When someone wants to talk to us, the completion routine for the PPCInform |
call gets called. That's this routine. When we get called, it means the |
CDEV wants to talk to us. We find out what it's asking by looking at the |
"userData" field, in which the CDEV has placed a message number. We only |
know kGetCommonGlobalsPtr, so we respond to that by returning the pointer |
to our public globals. After filling out the parameter block and i/o |
buffer, we make a PPCWrite call. We are called at interrupt time (we are a |
completion routine, remember), so we make the PPCWrite call |
asynchronously. We provide a completion routine that gets executed when |
the call completes. |
Because this is a completion routine, it is declared as using the Pascal |
calling convention. |
------------------------------------------------------------------------------*/ |
pascal void InformCompProc(PPCParamBlockPtr ppb) |
{ |
OSErr err; |
GetCommonGlobalsPtr myBuffer; |
myBuffer = (GetCommonGlobalsPtr) ((SessionPtr)ppb)->buffer; |
ppb->writeParam.ioCompletion = (PPCCompProcPtr) WriteCompProc; |
ppb->writeParam.bufferLength = 0; |
ppb->writeParam.bufferPtr = (Ptr) myBuffer; |
ppb->writeParam.more = false; |
switch (ppb->informParam.userData) { |
case kGetCommonGlobalsPtr: |
ppb->writeParam.bufferLength = sizeof(GetCommonGlobalsRecord); |
myBuffer->commonGlobalsAddress = &gCommonGlobals; |
break; |
} |
err = PPCWriteAsync(&ppb->writeParam); |
} |
/*------------------------------------------------------------------------------ |
pascal void WriteCompProc(PPCParamBlockPtr ppb) |
When our PPCWrite routine completes, the PPCToolbox calls this completion |
routine. All this is responsible for it closing the session on this end by |
calling PPCEnd. |
Because this is a completion routine, it is declared as using the Pascal |
calling convention. |
------------------------------------------------------------------------------*/ |
pascal void WriteCompProc(PPCParamBlockPtr ppb) |
{ |
OSErr err; |
ppb->endParam.ioCompletion = (PPCCompProcPtr) EndCompProc; |
err = PPCEndAsync(&ppb->endParam); |
} |
/*------------------------------------------------------------------------------ |
pascal void EndCompProc(PPCParamBlockPtr ppb) |
After the PPCEnd call completes, the PPCToolbox calls this completion |
routine. This routine is responsible for setting us up for receiving more |
PPC communications requestions. We also call this routine when we want to |
set ourselves for the very first PPCInform call. |
Because this is a completion routine, it is declared as using the Pascal |
calling convention. |
------------------------------------------------------------------------------*/ |
pascal void EndCompProc(PPCParamBlockPtr ppb) |
{ |
OSErr err; |
ppb->informParam.ioCompletion = (PPCCompProcPtr) InformCompProc; |
ppb->informParam.autoAccept = true; |
ppb->informParam.portName = &((SessionPtr)ppb)->portName; |
ppb->informParam.locationName = &((SessionPtr)ppb)->locationName; |
ppb->informParam.userName = &((SessionPtr)ppb)->userName; |
err = PPCInformAsync(&ppb->informParam); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14