Keyspan TN1119 User Manual

TN 1119: Serial Port Apocrypha Page: 1
NOTE: This Technical Note has been retired. Please see the Technical Notes page for current documentation.
CONTENTS
Notes for Both APIs
Just The Facts: Classic Serial
Just The Facts: OT Serial
A Tale of Two Arbitrators
References
Downloadables
This Technote describes a number of problems often encountered by developers when dealing with serial ports under Mac OS. Most of this information is available from other sources, but those sources are obscure and commonly overlooked.
Specifically, this Note describes the correct techniques for finding, opening, closing, and yielding serial ports under the classic serial API and the Open Transport serial API. In addition, this Note describes the theory and practice of the original and Open Transport serial port arbitrators.
This Technote is directed at all Mac OS developers who use serial ports.
Updated: [Apr 23 1998]
Notes for Both APIs
Mac OS provides two APIs for accessing the serial port: the classic serial API based on Device Manager 'DRVR's, described in Inside
Macintosh:Devices , and the Open Transport serial API, described in Inside Macintosh: Open Transport . This section contains notes
which are relevant to both serial APIs.
Open and Close on Demand
A serial port is a non-sharable resource. If your application has the port open, no other application can open it. For this reason, you should always open and close the serial port on demand.
For example, if your application only uses the serial port as part of its registration process, you open the port when you commence the registration and close the port immediately after you are done.
Yielding
Yielding is the process by which a passive serial program can yield the serial port to an active serial program, and regain the serial port after the active serial program is done.
For example, if you set Apple Remote Access (version 2.1 and lower) to wait for an incoming call, you can still make outgoing PPP connections using FreePPP. This is because the passive serial program (ARA) yields the serial port to the active serial program (FreePPP). When FreePPP closes the serial port, ARA will resume ownership and continue waiting for an incoming call.
Back to top
TN 1119: Serial Port Apocrypha Page: 2
Just The Facts: Classic Serial
The classic serial architecture is based on Device Manager 'DRVR's, as described in Inside Macintosh:Devices . This section describes the correct way to find, open, close, and yield serial ports under the classic serial architecture.
Finding All Serial Ports
The correct way to find all the serial ports under Mac OS is to use the Communications Resource Manager (CRM) routine CRMSearch (part of the Communications Toolbox). Unfortunately, the book that documents the Communications Resource Manager (Inside the
Macintosh Communications Toolbox ) is not available in electronic form, so it can be hard to find documentation for CRMSearch. The
following sample is included to make up for this deficiency:
static void PrintInfoAboutAllSerialPorts(void) // Prints a list of all the serial ports on the // machine, along with their corresponding input // and output driver names, to stdout. Typically // you would use a routine like this to populate a // popup menu of the available serial ports. { CRMRec commRecord; CRMRecPtr thisCommRecord; CRMSerialPtr serialPtr;
(void) InitCRM();
// First set up commRecord to specify that // we're interested in serial devices.
commRecord.crmDeviceType = crmSerialDevice; commRecord.crmDeviceID = 0;
// Now repeatedly call CRMSearch to iterate // through all the serial ports.
thisCommRecord = &commRecord; do { thisCommRecord = (CRMRecPtr) CRMSearch( (CRMRecPtr) thisCommRecord ); if ( thisCommRecord != nil ) {
// Once we a have a CRMRec for the serial port, // we must cast the crmAttributes field to // a CRMSerialPtr to access to serial-specific // fields about the port.
serialPtr = (CRMSerialPtr) thisCommRecord->crmAttributes;
// Print the information about the port.
printf("We have a port called: '%#s'\n", *(serialPtr->name)); printf(" input driver named: '%#s'\n", *(serialPtr->inputDriverName)); printf(" output driver named: '%#s'\n", *(serialPtr->outputDriverName)); printf("\n");
// Now ensure that CRMSearch finds the next device.
commRecord.crmDeviceID = thisCommRecord->crmDeviceID; } } while ( thisCommRecord != nil ); }
TN 1119: Serial Port Apocrypha Page: 3
Note:
The CRM is available in System 7.0 and later. It is an installable option under System 6. If your product runs under System 6, you should check for the presence of the CRM by calling Gestalt with the
gestaltCRMAttr selector and checking that the gestaltCRMPresent bit is set in the response.
IMPORTANT:
Ports registered with the CRM are supposed to "work like" the standard built-in serial ports. However, in some cases (both Apple and third party), it's just not possible to implement the API of the built-in serial ports exactly. When dealing with CRM-registered ports, your application should handle cases where this emulation breaks down. For example, if your application uses the externally clocked quasi-MIDI mode (csCode 15), it should gracefully fail if a serial driver returns an error when asked to engage this mode.
Opening a Serial Port
The correct way to open a serial port has been documented for many years as part of the ARA API
document, currently available on the Mac OS SDK Developer CDs. However, this source is somewhat obscure (and the enclosed sample code is somewhat out of date), so the information is repeated here for your convenience.
The process is very easy to describe in English:
If a serial port arbitrator is installed, always call OpenDriver to open the serial port; otherwise, walk the unit table to determine whether the driver is already open, and open it only if it isn't.
This high-level algorithm is captured in the following routines for opening both the input and output serial drivers:
static OSErr OpenOneSerialDriver(ConstStr255Param driverName, short *refNum) // The one true way of opening a serial driver. This routine // tests whether a serial port arbitrator exists. If it does, // it relies on the SPA to do the right thing when OpenDriver is called. // If not, it uses the old mechanism, which is to walk the unit table // to see whether the driver is already in use by another program. { OSErr err;
if ( SerialArbitrationExists() ) { err = OpenDriver(driverName, refNum); } else { if ( DriverIsOpen(driverName) ) { err = portInUse; } else { err = OpenDriver(driverName, refNum); } } return err; }
static OSErr OpenSerialDrivers(ConstStr255Param inName, ConstStr255Param outName, SInt16 *inRefNum, SInt16 *outRefNum) // Opens both the input and output serial drivers, and returns their // refNums. Both refNums come back as an illegal value (0) if we // can't open either of the drivers. { OSErr err;
err = OpenOneSerialDriver(outName, outRefNum); if (err == noErr) { err = OpenOneSerialDriver(inName, inRefNum);
TN 1119: Serial Port Apocrypha Page: 4
if (err != noErr) { (void) CloseDriver(*outRefNum); } } if (err != noErr) { *inRefNum = 0; *outRefNum = 0; } return err; }
The above code opens the output serial driver before opening the input serial driver. This is the recommended order for the built-in serial drivers, and consequently for other CRM-registered serial drivers. This is because the output driver is the one that reserves system resources and actually checks for the availability of the port. For the built-in serial ports, if you successfully open the output driver, you should always be able to open the input driver. Not all CRM-registered serial drivers work this way, however, so your code should always check the error result from both opens.
The code for determining whether a serial port arbitrator is installed is shown below:
enum { gestaltSerialPortArbitratorAttr = 'arb ',
gestaltSerialPortArbitratorExists = 0 };
static Boolean SerialArbitrationExists(void) // Test Gestalt to see if serial arbitration exists // on this machine. { Boolean result; long response;
result = ( Gestalt(gestaltSerialPortArbitratorAttr, &response) == noErr && (response & (1 << gestaltSerialPortArbitratorExists) != 0) != 0) ); return result; }
The final part of the puzzle is the routine DriverIsOpen, which walks the unit table to see if the driver serial driver is present and open. Remember that this routine -- which is inherently evil because it accesses low memory globals -- is only used if a serial port
arbitrator is not installed.
Loading...
+ 7 hidden pages