BE ENGINEERING INSIGHTS: The Configuration Manager, Part I
By Victor Tsou

The configuration manager, first introduced in BeOS Release 3, has been rewritten for Release 4. It is primarily used for supporting ISA Plug-and-Play devices, although its services will also be enlisted to help integrate PCMCIA. The HOTD is <drivers/config manager.h>. Stare at it, then continue reading.

The configuration manager provides five classes of services:

  1. Initializing and uninitializing.
  2. Enumerating and reporting information about devices.
  3. Reporting the current configuration for devices.
  4. Interpreting configuration information.
  5. Reporting possible configurations for devices.

1. Initializing and uninitializing.

The configuration manager is implemented as a module, a construct introduced in R4 that is accessible only from kernel space. A module exposes itself to drivers via a structure of function pointers in a bid to be a better ioctl than ioctl. Modules are loaded as needed and unloaded when no longer used. To initialize the configuration manager, simply load the module in the usual fashion:

config manager for driver module info *module;

if (get module(B CONFIG MANAGER FOR DRIVER MODULE NAME,
    (module info **)&module) < 0)
  return B ERROR;

Conversely, to uninitialize:

put module(B CONFIG MANAGER FOR DRIVER MODULE NAME);

Out of respect for reference counting, your driver should maintain a one-to-one correspondence between calls to get module and calls to put module.

2. Enumerating and reporting information about devices.

Once the configuration manager has been loaded, your device driver will feel compelled to scan for devices it knows about. The function

  status t (*get next device info)(
      bus type bus,
      uint64 *cookie,
      struct device info *info,
      uint32 info size);

iterates through the devices on the specified bus, placing info size bytes of information about each device in a device info structure. For example, the following loop runs through the ISA devices:

/* cookie = 0 signals start of enumeration */
uint64 cookie = 0;
struct device info info;


while (module->get next device info( B ISA BUS, &cookie, &info, sizeof(struct device info)) == B OK) { ... }

For completeness, the device info declaration:

struct device info {
  /* Size in bytes of bus-independent and
   * bus-dependent data for this device */
  uint32    size;


  /* Offset, relative to the start of the structure,
   * to the bus-dependent data for the device */
  uint32    bus dependent info offset;


  /* B ISA BUS or B PCI BUS */
  bus type  bus;


  /* Device code, a la PCI */
  device type  devtype;


  /* "Normally unique and persistent" id for the
   * device */
  uint32    id[4];


  /* Device-independent flags */
  uint32    flags;


  /* If config status is B OK, the device is working
   * properly. Otherwise, the device is disabled and
   * should be ignored by device drivers */
  status t    config status;
};

The configuration manager will report up to size bytes of information about the device. This includes both bus-independent data, as described by the device info structure, and bus-dependent data, which appear beginning at offset bus dependent info offset in the returned buffer. Since you can't know how large the device information buffer should be until after you've read it, the configuration manager provides an additional function to load the device info for a specific device:

  status t (*get device info for)(
      uint64 device,
      struct device info *info,
      uint32 len);

The revised loop now looks like this (with error checks stripped out):

while (module->get next device info(B ISA BUS, &cookie,
    &info, sizeof(device info)) == B OK) {
  struct device info *dinfo;
  struct isa info *iinfo;


  /* only worry about configured devices */
  if (config status != B OK) continue;


  dinfo = malloc(info.size);
  module->get device info for(
      cookie, &dinfo, info.size);
  iinfo = (struct isa info *)((char *)dinfo +
      info.bus dependent info offset);
  ...
  free(dinfo);
}

ISA bus-dependent information is stored as isa info, defined in <drivers/isapnp.h>, and PCI-specific data are stored as pci info, defined in <drivers/PCI.h>. A driver typically peeks at the bus-dependent information to determine whether it can control a particular device.

3. Reporting the current configuration for devices.

Once a driver has identified a known device, it must fetch the device's configuration. This is done via a pair of functions:

  status t (*get size of current configuration for)(
      uint64 device);
  status t (*get current configuration for)(
      uint64 device,
      struct device configuration *config,
      uint32 len);

These functions are usually called this way:

status t result;
struct device configuration *config;


result = module->get size of current configuration for(cookie);
if (result < 0) return B ERROR;
config = malloc(result);
if (!config) return B ERROR;
if (module->get current configuration for(
    cookie, config, result) < B OK) {
  free(config);
  return B ERROR;
}
...
free(config);

The device configuration is an array of resource descriptors representing the resources (IRQs, DMAs, I/O ports, and memory) assigned to the device. Resource descriptors come in two flavors. IRQs and DMAs are described as masks, while I/O ports and memory are described as ranges. Masks are bitfields, with the nth bit representing the nth IRQ or DMA. Exactly one of the bits will be set in each mask. Ranges are described by two values, the minbase and the len, with the range running from minbase to minbase + len - 1, inclusive.

4. Interpreting configuration information.

The following routines help drivers wade through device configurations:

  status t (*count resource descriptors of type)(
      const struct device configuration *config,
      resource type type);
  status t (*get nth resource descriptor of type)(
      const struct device configuration *config,
      uint32 n,
      resource type type,
      resource descriptor *descriptor,
      uint32 descriptor size);

These function precisely as their names and prototypes suggest.

5. Reporting possible configurations for devices.

The configuration manager selects configurations for devices based on sets of possible configurations reported by each device. Drivers typically won't ever need to access these configurations; they're provided for the benefit of device management utilities such as the new Devices preference application.

  status t (*get size of possible configurations for)(
      uint64 device);
  status t (*get possible configurations for)(
      uint64 device,
      struct possible device configurations *possible,
      uint32 len);

possible device configurations is an array of device configuration structures, with each element representing a set of possible configurations. Since the size of a device configuration is variable, you can't access individual device configurations by directly indexing the array. Instead, you have to walk the structure manually:

#define NEXT POSSIBLE(c) \
  (c) = (struct device configuration *) \
    ((uchar *)(c) + \
    sizeof(struct device configuration) + \
    (c)->num resources * \
    sizeof(resource descriptor))


struct device configuration *config =
    possible->possible + 0;


for (i=0;i<possible->num possible;i++)
{
  ...
  NEXT POSSIBLE(config);
}

Descriptors for possible configurations differ slightly from their current configuration counterparts since they represent a set of possible choices rather than a single selection. Masks may have multiple bits set, with each bit representing a possible IRQ or DMA setting. Ranges are described by a minbase, a maxbase, a basealign, and a len, describing a range starting between minbase and maxbase, in increments of basealign, and having length len.

Most APIs make more sense when you see them in action, so next time we'll develop an application demonstrating the use of the configuration manager.

Copyright ©1998 Be, Inc. Be is a registered trademark, and BeOS, BeBox, BeWare, GeekPort, the Be logo and the BeOS logo are trademarks of Be, Inc. All other trademarks mentioned are the property of their respective owners. Comments about this site? Please write us at webmaster@be.com.