Accessing a Serial Port from User Space

To a user space application, a serial port driver is accessed as a standard character device in the /dev directory. This should be familiar territory for anyone who has accessed a serial port on any other UNIX system. Where the I/O Kit approach differs, however, is in how a user space application enumerates the serial ports that are present in a system. For many traditional UNIX applications, the user must specify the full path of the serial port's character file. The approach taken by Mac OS X is to shield users from the /dev directory, and to present available serial ports through a descriptive name. This is where the I/O Kit comes in.

Since a serial port is implemented by an I/O Kit driver, its driver object can be found by user space applications in the I/O Registry, as described in Chapter 5. Like all entries in the I/O Registry, the entry for a serial port driver contains a property table that can be used to obtain a descriptive name for the serial port, and a full path to the serial port's character device file. Having obtained the path to the serial port's device file, the user space application can then proceed to open and access the device, as would be done by a traditional UNIX program.

As with any application that wishes to locate a driver through the I/O Registry, the first step in finding a serial port driver is to create a matching dictionary. The role of a matching dictionary is to locate entries in the I/O Registry that meet certain criteria, and filter out all other entries. A user space process accesses a serial port not through the serial port driver itself, but rather through the driver's associated IOSerialBSDClient class. Therefore, to find serial ports in the system, a user space process just needs to create a matching dictionary to find all IOSerialBSDClient objects in the registry. This can be done as follows:

#include <IOKit/serial/IOSerialKeys.h>

CFMutableDictionaryRef    matchingDict;
matchingDict = IOServiceMatching(kIOSerialBSDServiceValue);

To further refine the matches, the user process can add the key kIOSerialBSDTypeKey to the matching dictionary, and limit the results to modem devices (serial drivers that created an IOModemSerialStreamSync object) or generic serial port devices (serial drivers that created an IORS232SerialStreamSync object). For example, to limit the matches to modem devices, a user space application would create the following matching dictionary:

matchingDict = IOServiceMatching(kIOSerialBSDServiceValue);
CFDictionarySetValue(matchingDict, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDModemType));

Having created a matching dictionary to locate the serial devices that it is interested in, the process is then able to iterate the registry for drivers that meet the criteria specified by that dictionary. All instances of IOSerialBSDClient contain registry properties that are specific to a serial port driver, namely:

  • kIOTTYDeviceKey: a CFStringRef containing a descriptive name for the serial port
  • kIOCalloutDeviceKey: a CFStringRef containing the full path to the callout character device file for the serial port
  • kIODialinDeviceKey: a CFStringRef containing the full path to the dial-in character device file for the serial port

To show how these properties can be used, the code in Listing 11-9 demonstrates how to enumerate all serial devices in the system and how to open each device.

Listing 11-9. A Sample Application That Uses the I/O Kit to Enumerate all Serial Devices Present in the System and Find the Path of the Character Device for each Serial Port

#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/serial/IOSerialKeys.h>
#include <sys/param.h>
#include <fcntl.h>
#include <unistd.h>

int main (int argc, const char * argv[])
{
       CFMutableDictionaryRef             matchingDict;
       io_iterator_t                      iter = 0;
       io_service_t                       service = 0;
       kern_return_t                      kr;
       
       // Create a matching dictionary that will find any serial device
       matchingDict = IOServiceMatching(kIOSerialBSDServiceValue);
       kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter);
       if (kr != KERN_SUCCESS)
              return -1;
       
       // Iterate over all matching objects
       while ((service = IOIteratorNext(iter)) != 0)
       {
              CFStringRef           cfDeviceName;
              CFStringRef           cfCalloutPath;
              Char                  deviceName[256];
              Char                  calloutPath[MAXPATHLEN];
              Int                   fd;
              
              // Get the device name
              cfDeviceName = IORegistryEntryCreateCFProperty(service, CFSTR(kIOTTYDeviceKey),
                                    kCFAllocatorDefault, 0);
              CFStringGetCString(cfDeviceName, deviceName, sizeof(deviceName),
                                    kCFStringEncodingUTF8);
              CFRelease(cfDeviceName);
              
              // Get the character device path
              cfCalloutPath = IORegistryEntryCreateCFProperty(service,
                                    CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
              CFStringGetCString(cfCalloutPath, calloutPath, sizeof(calloutPath),
                                    kCFStringEncodingUTF8);
              CFRelease(cfCalloutPath);
              
              // The I/O Registry object is no longer needed
              IOObjectRelease(service);
              
              // Proceed to open and use the device at "calloutPath" as usual
              printf("Found device %s at path %s ", deviceName, calloutPath);
              
              fd = open(calloutPath, O_RDWR | O_NOCTTY | O_NONBLOCK);
              // Clear the O_NONBLOCK flag so subsequent I/O will block
              fcntl(fd, F_SETFL, 0);
              
              // Configure serial device with tcsetattr()
              // Read and write with read() / write()
              
              close(fd);
       }
       
       // Release the I/O Registry iterator
       IOObjectRelease(iter);
       
       return 0;
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.121.54