http://www.developer.com/

Back to article

How Palm OS Expands Your Applications: Expansion Manager


June 23, 2005

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 (see the previous article ). The next sections will guide you through the stuff available for applications. But, right here I would like to highlight one simple but important thing: Prior to using any functions from Virtual File System Manager or Expansion Manager, your application must verify whether those features are present. This is as easy as a couple of code lines:


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.

Inserting and Removing Cards

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:

  1. The sysNotifyCardInsertedEvent is broadcast by the slot driver
  2. Expansion Manager, registered for this event with priority 20, tries to mount volumes on inserted card with VFSVolumeMount
  3. VFSVolumeMount broadcasts sysNotifyVolumeMountedEvent for each mounted volume
  4. Expansion Manager, registered for this event with priority -20, does its job before any other application with normal priority
  5. VFS Manager, registered with priority 10, handles next two steps
  6. VFS Manager copies start.prc (if such an application exists in the /PALM folder)
  7. sysAppLaunchCmdCardLaunch is sent to start.prc and then sysAppLaunchCmdNormalLaunch
  8. Expansion Manager plays a sound indicating card insertion

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:

  • expHandledVolume to prevent Expansion Manager; to deal with volume mounting and unmounting
  • expHandledSound to prevent Expansion Manager; to deal with sound indication on card insertion/removal

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:

  • vfsHandledUIAppSwitch to prevent VFS Manager from performing an UI switch to start.prc
  • vfsHandledStartPrc to prevent VFS Manager from running start.prc automatically

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:


...
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;
}

Conclusion

Palm OS Expansion and VFS Managers working together give additional power to your application. You now can use their APIs to handle data stored on various expansion cards and similar peripheral devices. I hope that this article series has helped you in getting a general picture of what's going on in this area. Definitely some cards can behave weirdly, but I hope you will successfully conquer all possible issues....

About the Author

Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers lives in the mobile jungles a little bit simpler.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date