How Palm OS Expands Your Applications: Expansion Manager
Expansion Manager, to confirm its name, is a layer that manages slot drivers. Actually, it doesn't work with slots directly. Instead, Expansion Manager provides unified APIs to manupilates slots. All specific 'magic is performed by appropriate slot driver implemented as a shared library. Such architecture is similar to VFS Manager .
UInt32 nValue = 0;
if ( ftrErrNoSuchFeature == FtrGet(sysFileCExpansionMgr,
expFtrIDVersion, &nValue) )
{
// Nothing to do...
}
else
{
// Check here required minimal Expansion Manager Version
}
Slot Enumeration and So Forth
As with many other managers, the very first step is usually some kind of enumeration process. Your application needs to know which slots are presented in the system. Hence, the slot number is a base stone in all further operations. The following tiny snippet shows all the required code to obtain available slots:
UInt16 slotRefNum = -1;
UInt32 slotIterator = expIteratorStart;
while (slotIterator != expIteratorStop)
{
if ( errNone == (err = ExpSlotEnumerate(&slotRefNum,
&slotIterator)) )
{
// slotRefNum to be used later
}
else
{
// error occured
}
}
After the slot number is detected, there are a number of operations you can accomplish. I will start with the simplest one:
Err ExpCardPresent(UInt16 slotRefNum) This function verifies whether a card is presented into specified slot. errNone indicates success; in other words, the card is in. All other return codes will flag appropriate an error; for example, expErrCardNotPresent.
Your application may need to gather more info about a card in a slot. The following simple function provides this data:
Err ExpCardInfo(UInt16 slotRefNum, ExpCardInfoType *infoP) A code sample below displays one possible scenario:
/* Excerpt from ExpansionMgr.h
typedef struct ExpCardInfoTag
{
// bits for different stuff the card supports
UInt32 capabilityFlags;
// Manufacturer, e.g., "Palm", "Motorola", etc...
Char manufacturerStr[expCardInfoStringMaxLen+1];
// Name of product, e.g., "SafeBackup 32MB"
Char productStr[expCardInfoStringMaxLen+1];
// Type of product, e.g., "Backup", "Ethernet", etc.
Char deviceClassStr[expCardInfoStringMaxLen+1];
// Unique identifier for product, e.g., a serial number.
// Set to "" if no such identifier exists.
Char deviceUniqueIDStr[expCardInfoStringMaxLen+1];
}
ExpCardInfoType, *ExpCardInfoPtr;
*/
...
ExpCardInfoType CardInfo;
if( errNone == ExpCardInfo(slotRefNum,&CardInfo))
{
HostTraceOutputTL(appErrorClass,"Capability: %snManufacturer:
%snName: %snClass: %s",
(const char*)GetCapability(CardInfo.capabilityFlags),
CardInfo.manufacturerStr,
CardInfo.productStr,
CardInfo.deviceClassStr);
}
else
{
// process error
}
...
CString GetCapability(UInt32 capabilityFlags)
{
CString sInfo;
if ( capabilityFlags & expCapabilityHasStorage )
sInfo += " Has Storage";
if ( capabilityFlags & expCapabilityReadOnly )
sInfo += " Read Only";
if ( capabilityFlags & expCapabilitySerial )
sInfo += " Serial Interface";
return sInfo;
}
As you can see, the received struct contains important info regarding card capabilities and class, so you always will be able to determine what your application can do with this particular storage or whatever it is.
If the expansion card supports simple serial interface, you can obtain a creator ID to use it later with Serial I/O API calling:
Err ExpCardGetSerialPort(UInt16 slotRefNum, UInt32 *portP) A typical usage is as follows:
UInt32 port;
if ( errNone == ExpCardGetSerialPort(slotRefNum, &port) )
{
UInt16 newPortId;
err = SrmOpen (port, 19200, newPortId);
...
}
In Palm OS 6 Cobalt, ExpCardGetSerialPort and related stuff were deleted, so yoo should check which OS version you're going to support in your applications.
When expansion cards are inserted to or removed from a slot, Palm OS sends several broadcast notifications to inform all interested parties about the event that occured. Usually, Expansion and VFS Managers register several events on the card's insertion/removal, volume mounting, and so forth. Your application may be required to intercept default handlers. Palm OS gives you a simple way to do it: using notification handlers. A common practice here is to register for desired notifications at program startup and then unregister when it is no longer needed, usually at the exit from the application. The default sequence of broadcasted events and related stuff upon card insertion are listed below: Looking at the above list, let me point to where your application can intercept a default flow. There are two main spots. When the user inserts a card into expansion slot, sysNotifyCardInsertedEvent is broadcast. By default, Expansion Manager registers itself to get it with priority 20. This is done to ensure that Expansion Manager will get notified after all other applications registered for the same event with normal priority (sysNotifyNormalPriority). Your new handler should set the appropriate bits in the SysNotifyParamType.handled member: If you need only to be notified about these events without any influence on the default flow, just don't set up these two bits, that's all. Applications usually register themselves to volume mount/unmount events rather than to card related stuff, which was initially intended for system use. Nevetherless, sometimes you can be required to handle such kind of events as well. A similar event chain happens in the case of sysNotifyVolumeMountedEvent. As you have seen already, VFS Manager registers itself to receive volume-related notifications with priority 10. So, if your application was registered for such notifications with normal priority, you always will be able to change the default flow of event handling. Saying so, you might be setting the following bits in the SysNotifyParamType.handled member: The volume unmounting and card removal processes flow is similar to insertion, but with several differences. Upon card removal, Expansion Manager informs all subscribers by broadcasting sysNotifyCardRemovedEvent, unmounts all its volumes, and plays a sound to indicate removal. Volume unmounting causes VFS Manager to remove start.prc from heap and send sysNotifyVolumeUnmountedEvent. Your application can be interested to be notified about such events. The last, but not the least thing, is the security issues here. You should keep in mind one very simple scenario. If the PDA was locked at the time of insertion/mounting notification, your application has not run anything, thus keeping the PDA locked. Finally, after such a long discussion about different notification codes, let me place a simple schematical code snippet here to illustrate all the things you have learrned:Inserting and Removing Cards
...
static Err AppStart(void)
{
UInt16 cardNo;
LocalID dbID;
// Get the current application path
SysCurAppDatabase(&cardNo, &dbID);
// Register for desired notifications, they will be sent as
// launch codes (the fourth parameter is NULL)
SysNotifyRegister(cardNo, dbID, sysNotifyCardInsertedEvent,
NULL, sysNotifyNormalPriority, NULL);
SysNotifyRegister(cardNo, dbID, sysNotifyCardRemovedEvent,
NULL, sysNotifyNormalPriority, NULL);
SysNotifyRegister(cardNo, dbID, sysNotifyVolumeMountedEvent,
NULL, sysNotifyNormalPriority, NULL);
SysNotifyRegister(cardNo, dbID, sysNotifyVolumeUnmountedEvent,
NULL, sysNotifyNormalPriority, NULL);
...
return errNone;
}
static void AppStop(void)
{
UInt16 cardNo;
LocalID dbID;
// Get the current application path
SysCurAppDatabase(&cardNo, &dbID);
// Unregister for all notifications
SysNotifyUnregister(cardNo, dbID, sysNotifyCardInsertedEvent,
sysNotifyNormalPriority);
SysNotifyUnregister(cardNo, dbID, sysNotifyCardRemovedEvent,
sysNotifyNormalPriority);
SysNotifyUnregister(cardNo, dbID, sysNotifyVolumeMountedEvent,
sysNotifyNormalPriority);
SysNotifyUnregister(cardNo, dbID, sysNotifyVolumeUnmountedEvent,
sysNotifyNormalPriority);
...
/* Close all the open forms. */
FrmCloseAllForms();
}
...
UInt32 PilotMain(UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags)
{
Err error;
UInt32 locked = 0;
SysNotifyParamType *notifyParamP = NULL;
UInt16 nSlotNumber = 0;
error = RomVersionCompatible (ourMinVersion, launchFlags);
if (error) return (error);
switch (cmd)
{
case sysAppLaunchCmdNormalLaunch:
error = AppStart();
if (error)
return error;
/*
* start application by opening the main form
* and then entering the main event loop
*/
FrmGotoForm(MainForm);
AppEventLoop();
AppStop();
break;
case sysAppLaunchCmdNotify:
notifyParamP = (SysNotifyParamType *)cmdPBP;
switch(notifyParamP->notifyType)
{
case sysNotifyCardInsertedEvent:
nSlotNumber = (UInt16)notifyParamP->notifyDetailsP;
// do whatever you need to
break;
case sysNotifyCardRemoveEvent:
nSlotNumber = (UInt16)notifyParamP->notifyDetailsP;
// do whatever you need to
break;
case sysNotifyVolumeMountedEvent:
nSlotNumber = (UInt16)notifyParamP->notifyDetailsP;
// do whatever you need to
break;
case sysNotifyVolumeUnmountedEvent:
nSlotNumber = (UInt16)notifyParamP->notifyDetailsP;
// do whatever you need to
break;
...
}
break;
}
return errNone;
}
|