Embedded Rendezvous: A Developer's Journeyby Todd Wade
The company that I work for designs and manufactures test equipment for the telephony and cable industries. This equipment nearly always sports at least a 32-bit micro-controller running some form of RTOS. Equally often, the products will carry a floating-point DSP to do the numeric heavy lifting.
Increasingly, these products are backplane oriented, where multiple test cards plug into a chassis. Those cards need to communicate amongst each other and back to a central controller card within an isolated internal network. As a result, we have a need for a common scheme for advertising each card's presence and capabilities. These chassis are designed to allow cards with varying functionality to be plugged in. As these cards are added or removed, it's important for the system as a whole to know this and to know what functionality has either been just added or removed. Add to all that the need for each chassis to communicate with other similar chassis, and what you get is a perfect environment for technology like Rendezvous.
I won't detail each of the features of Rendezvous, but I will say that it offers a number of attractive features for embedded systems like ours. First, instead of inventing some scheme for device and service discovery, we now have the tools already built. Also, as the technology of Rendezvous develops, we can easily keep apace of the changes. Should new features become available, we can very quickly benefit because we've made use of openly available source code. The obvious benefit of numerous "pairs of eyes" looking at such code helps to assure its continued quality, and this definitely provides us a testing resource that we could never have had internally.
Embedded systems typically have limitations that workstation-class hardware perhaps doesn't:
- Frequently, there is no standard file system.
- Free memory is often a rare commodity.
- Processing power is frequently limited.
- Multiple tasks may or may not exist and when they do, there may often be requirements not ordinarily associated with workstation machines.
- Embedded systems often implement subsets of various standard features. For example, many embedded systems do not support a complete implementation of the TCP/IP protocol stack.
- There is often a single memory address space that is shared by all entities within the product.
We had to deal with all of these issues (among others) during our port of Rendezvous to our embedded platform. This article (and subsequent installments) is an attempt to detail some of our experiences and to relate some of the potential pitfalls that can occur when attempting to make use of the Rendezvous mDNS core code.
While we would love to say that the experience was painless, we can't. Fortunately, the pain of the experience has nothing to do with the quality or utility of Rendezvous itself. Rather, most of our difficulty arose from a fairly serious lack of development and application documentation. Out of this effort has come a "wrapper" library that allows for simple implementation of Rendezvous, without the need for detailed understanding of the core functionality of the Apple-provided source code. Along the way, we will describe this library, along with various Rendezvous APIs that pertain to our use of this excellent technology.
It's important to remember that our environment, while fairly rich by embedded system standards, is still one that very often cannot take full advantage of the resources provided by the likes of Rendezvous. Subsequently, our wrapper library reflects a minimalist approach, where only the features that are most applicable to our needs are implemented. That's not to say that you can't find it useful. Indeed, our library implements the greater part of the Rendezvous functionality and can be applied immediately. It's being released as open source, in the hope that as time progresses, it will become a full-featured package that can be used in the widest variety of cases.
Rendezvous came to our attention due to the fact that several of the developers on my team are Macintosh fans. When this technology was introduced in Mac OS X, we began to examine what it could do for our products. It was obviously an open source technology given to the world by Apple, but just how restrictive would it be to implement in a target that was decidedly "non-workstation" class?
As it turned out, Apple did an excellent job of isolating platform-specific aspects of the code from the body of its functionality. They break the world of Rendezvous into basically three sections: the user's application, the mDNSCore, and the platform-specific layer. These divisions make for a near-ideal environment for platforms like ours.
We were looking for some pretty simple features: the ability to:
- Advertise the presence of a device within our chassis.
- Advertise the presence of a chassis within a network.
- Advertise the IP addresses and port numbers of the services that were being offered.
- Arbitrarily describe services being offered.
Rendezvous seemed to give us just what we were looking for, so off to the Internet and Apple's web site to download the source.
Before going much further, if you've done much reading up on Rendezvous, perhaps you've noticed that we haven't discussed one of the aspects of the technology that seem as though we'd need it. This is link-local IP address resolution and local name resolution. The primary reason for not discussing it is that Rendezvous doesn't care how you got your IP address or what it is, for that matter. Our products resolve their IP addresses in a couple of different ways, and as a result, we don't need to make use of the link-local technique.
Anyway, we downloaded the Apple source and began to look at what we got. First, we built the Mac OS X and the POSIX versions of the demonstration applications to get a feel for how things look in a Rendezvous environment. Mind you, building the applications consisted of running MAKE for the POSIX version and Project Builder for the Mac OS X app. We didn't examine the source closely at that point.
Once we determined that things worked, it was time to begin examining the source to decide just what we needed to do to get it to work in our environment. However, before I describe our experience with porting it, it would be instructive to understand the environment under which it runs.
Our System Environment
Our system can properly be described as embedded. As such, it is intended to run without direct user intervention and sports no GUI, mouse, or other niceties such as disk drives, etc. As is usual for such goals, the system makes use of an embedded Real Time Operating System (RTOS) that manages the system by providing:
- Task creation and destruction.
- Memory management.
- Resource serialization.
These matters are best left to such products, and our application is no different.
So we have a system that has multiple tasks that all share a common memory space. Mind you, this is running on a 32-bit Motorola processor, so it's no slouch. It just doesn't have some of the more "workstation-class" features like hardware memory management, power management, hard disks, vast amounts of RAM, etc. For any given product, there may be any number of tasks running at a time that will very frequently need to communicate. In addition, tasks often need to interact with some form of file system, of which there could be many. Finally, our systems often need to be able to broadcast the occurrence of some notable event.
Our system, then, provides these features and more. What we do lack, compared to workstation-class devices, is negligible for the purposes of this discussion.
As I said earlier, we did the usual "download-build-debug-run" sequence when we first tried Rendezvous out. It seemed fairly clear from the modular nature of the mDNS core code that we could make use of the POSIX example code as a basis for our version. We started there and began to examine what features of the mDNS core we would need to satisfy our application needs. An abstract of the basic sequence for Rendezvous use generally follows this simple sequence.
First, initialize your target environment. This may consist of such things as initializing mutexes, setting the startup state of flags, allocation of memory, etc. Then initialize your instance of the mDNS core. This mainly consists of telling mDNS the following things:
- The address of its administration structure. This structure is used by mDNS to keep track of all of its states, enabled features, callback addresses, etc. You must provide persistent storage for this structure so that it is always available to mDNS core routines.
- The address of some application-defined object. This address is stored by mDNS core in its administration structure when you initialize it. This pointer is then available to your routines during Rendezvous callback routines, etc. You can use this for any purpose that suits your application's needs.
- The address of a cache of standard DNS resource records. mDNS uses this cache when it receives notice of services on the network.
- A Boolean indicating whether mDNS is to advertise local IP addresses.
- The address of a function to call when mDNS has completed initializing.
Register the service(s) that you want to offer with mDNS so that those services become available to other network entities. Decide what services that you are interested in knowing about (if any), and begin a "browse" for those services.
Now, clearly there is more to it than that, but this does a pretty good job of outlining the basics of what one might need to do to get Rendezvous running on your target.
'Til Next Time
I think that this is a good stopping point for this installment. Next time, I'll describe in detail the problems that we encountered as well as the TRYST (get it?) library that we built to help us use Rendezvous. With any luck, when this is all finished, you'll be able to put Rendezvous to good use with a minimum of work and pain.
Return to Mac DevCenter