TOC PREV NEXT INDEX

Writing Device Drivers for LynxOS


Device Resource Manager (DRM)

The Device Resource Manager (DRM) is a LynxOS module that functions as an intermediary between the operating system, device drivers, and physical devices and buses. The DRM provides a standard set of service routines that device drivers can use to access devices or buses without having to know device- or bus-specific configuration options. DRM services include device identification, interrupt resource management, device I/O to drivers, and device address space management. The DRM also supports dynamic insertion and deletion of devices.

This chapter introduces DRM concepts and explains DRM components. Sample code is provided for DRM interfaces and services. The PCI bus layer is described in detail with a sample driver and application. This chapter provides information on the following topics:

DRM Concepts

Device Tree

The Device Tree is a hierarchical representation of the physical device layout of the hardware. DRM builds a device tree during kernel initialization. The device tree is made up of nodes representing the I/O controllers, host bridges, bus controllers, and bridges. The root node of this device tree represents the system controller (CPU). There are two types of nodes in the device tree: DRM bus nodes and DRM device nodes.

DRM bus nodes represent physical buses available on the system, while DRM device nodes represent physical devices attached to the bus.

The DRM nodes are linked together to form parent, child, and sibling relationships. A typical device tree is shown in the figure below. To support Hot Swap environments, DRM nodes are inserted and removed from the device tree, mimicking Hot Swap insertion and extraction of system devices.

Device Tree

DRM Components

A module view of DRM and related components is shown in the following figure. A brief description of each module is given below the figure.

Module View

Bus Layer

DRM uses bus layer modules to support devices connected to many different kinds of buses. There are numerous bus architectures, many of which are standardized. Typical bus architectures seen in systems are the ISA, PCI, and VME standards, however, proprietary bus architectures also exist. DRM needs a specific bus layer module to support a specific kind of bus architecture. The device drivers use DRM service routines to interface to the bus layers. The bus layers interface with the BSP to get board-specific information.

The bus layers provide the following service routines to DRM:

LynxOS supports only one bus layer, which is used for managing PCI and CompactPCI devices. Some of the DRM functions described later in this chapter require the bus layer ID. The correct symbol to use is PCI_BUSLAYER.

DRM Nodes

A DRM node is a software representation of the physical device. Each node contains fields that provide identification, device state, interrupt routing, bus-specific properties, and links to traverse the device tree. DRM service routines are used to access the DRM node fields. These routines provide device drivers access to DRM facilities via a standard interface. This eliminates the need to know implementation details of the specific software structure. Some of the important fields of the DRM node are shown in the next table.

Note: Subsequent coding examples in this chapter make reference to a data structure of type drm_node_s. This structure is a data item used internally by the LynxOS kernel as the software representation of a DRM node and is not intended to be accessed at the driver or user level. LynxOS does not export a definition of this structure. The coding examples use opaque pointers, which are passed around and are not meant to be dereferenced.

DRM Node Fields  
Field Name
Description
vendor_id
This field is used for device vendor identification.
device_id
This field identifies the DRM node.
pbuslayer_id
This field identifies the primary bus layer of the bus/device node.
sbuslayer_id
This field identifies the secondary bus layer of a bus node.
node_type
This field indicates the node type--bus node or device node--and indicates if it is statically or dynamically configured.
drm_state
This field describes the life cycle state of the DRM node. DRM nodes include: IDLE, SELECTED, READY, or ACTIVE.
parent
This field links this node to its parent node. The root node has this field set to NULL to indicate that it has no parent.
child
This field links to the child node of this bus node. Only bus nodes have children.
sibling
This field links to the sibling node of this DRM node. The last sibling of a bus has this field set to NULL.
intr_flg
This field indicates if the device raises an interrupt to request service.
intr_cntlr
If the device uses interrupt service, this field indicates the controller to which the device is connected.
intr_irq
This indicates the interrupt request line of the controller to which this device is connected.
drm_tstamp
This field indicates when this node was created.
prop
This field links to bus-specific properties of the device.

DRM Node States

The status of a DRM node is indicated by its state. Initially, a DRM node is set to IDLE when it is created. Devices that are removed from the DRM tree, or undetected devices are considered UNKNOWN. The UNKNOWN state is not used by DRM, but the state is used to denote a device that is unrecognized to DRM. The following diagram details the stages of DRM node states.

DRM Node States

DRM Initialization

The DRM module is initialized during LynxOS kernel initialization. DRM builds a device tree of all visible devices and brings them up to a READY state, if possible. This is to enable all statically linked drivers to claim the DRM nodes and bring up the basic system service routines. Some DRM nodes may be left in the SELECTED state after kernel initialization is complete. Typically, this can be the result of unavailable resources.

LynxOS provides the ability to control PCI resource allocation. PCI resources can be allocated either by the BIOS or by DRM. By default, LynxOS x86 distributions use the BIOS to handle resource allocation. For other platforms, DRM handles the resource allocation. Because DRM uses the same set of interfaces, whether or not it handles resource allocation, device drivers do not need to change.

For more information on PCI resource allocation and DRM, see the chapter "PCI Resource Allocator for LynxOS" in the LynxOS User's Guide.

DRM Service Routines

DRM service routines are used by device drivers to identify, setup and manage device resources. Typically, they are used in the install() and uninstall() entry points of the device driver. Device drivers locate the device they need to service and obtain an identifying handle. This handle is used in subsequent DRM calls to reference the device. The table below gives a brief description of each service routine and typical usage. See the DRM man pages for more details. Additionally, see "Example Driver".

Summary of DRM Services  
Service
Description
Usage
drm_get_handle
Searches for a DRM node with a specific vendor and device identification and claims it for use.
All Drivers
drm_free_handle
Releases a DRM node and makes it READY.
All Drivers
drm_register_isr
Sets up an interrupt service routine.
All Drivers
drm_unregister_isr
Clears an interrupt service routine.
All Drivers
drm_map_resource
Creates an address translation for a device resource.
All Drivers
drm_unmap_resource
Removes an address translation for a device resource.
All Drivers
drm_device_read
Performs a read on a device resource.
All Drivers
drm_device_write
Performs a write on a device resource.
All Drivers
drm_locate
Locates and builds the DRM device tree. It probes for devices and bridges recursively and builds the DRM subtree.
General Device Management
drm_insertnode
Inserts a DRM node with specific properties. Only a single node is added to the DRM tree by this service routine.
General Device Management
drm_delete_subtree
Removes a DRM subtree. Only nodes in the IDLE state are removed.
General Device Management
drm_prune_subtree
Removes a DRM subtree. Nodes in the READY state are brought to the IDLE state and then deleted.
General Device Management
drm_select_node
Selects a node for resource allocation.
General Device Management
drm_select_subtree
Selects a DRM subtree for resource allocation. All the nodes in the subtree are SELECTED.
General Device Management
drm_unselect_node
Ignores a DRM node for resource allocation.
General Device Management
drm_unselect_subtree
Ignores an entire DRM subtree for resource allocation.
General Device Management
drm_alloc_resource
Allocates a resource to a DRM node or subtree.
General Device Management
drm_free_resource
Frees a resource from a DRM node or subtree.
General Device Management
drm_claim_handle
Claims a DRM node, given its handle. The DRM node is now ACTIVE.
General Device Management
drm_getroot
Gets the handle to the root DRM node.
General Device Management
drm_getchild
Gets the handle to the child DRM node.
General Device Management
drm_getsibling
Gets the handle to the sibling DRM node.
General Device Management
drm_getparent
Gets the handle to the parent DRM node.
General Device Management
drm_getnode
Gets the DRM node contents.
General Device Management
drm_setnode
Sets the DRM node contents.
General Device Management

Interface Specification

Device drivers call DRM service routines like any standard kernel service routine. The following table provides a synopsis of the service routines and their interface specification. Refer to LynxOS man pages for a complete description.

DRM Service Routine Interface Specification  
Name
Synopsis
drm_locate()
int drm_locate(struct drm_node_s *handle)
drm_insertnode()
int drm_insertnode(struct drm_node_s
*parent_node, void *prop,
struct drm_node_s **new_node)
drm_delete_subtree()
int drm_delete_subtree(struct drm_node_s *handle)
drm_prune_subtree()
int drm_prune_subtree(struct drm_node_s *handle)
drm_select_subtree()
int drm_select_subtree(struct drm_node_s *handle)
drm_unselect_subtree()
int drm_unselect_subtree(struct drm_node_s
*handle)
drm_select_node()
int drm_select_node(struct drm_node_s *handle)
drm_unselect_node()
int drm_unselect_node(struct drm_node_s *handle)
drm_alloc_resource()
int drm_alloc_resource(struct drm_node_s *handle)
drm_free_resource()
int drm_free_resource(struct drm_node_s *handle)
drm_get_handle()
int drm_get_handle(int buslayer_id,
int vendor_id,
int device_id, struct drm_node_s **handle)

drm_claim_handle()
int drm_claim_handle(struct drm_node_s *handle)
drm_free_handle()
int drm_free_handle(struct drm_node_s *handle)
drm_register_isr()
int drm_register_isr(struct drm_node_s
*handle, void (isr)(), void *args)
drm_unregister_isr()
int drm_unregister_isr(struct drm_node_s *handle)
drm_map_resource()
int drm_map_resource(struct drm_node_s *handle,
int resource_id, addr_t *vaddr)
drm_unmap_resource()
int drm_unmap_resource(struct drm_node_s *handle,
int resource_id)
drm_device_read()
int drm_device_read(struct drm_node_s *handle,
int resource_id,
unsigned int offset, unsigned int size,
void *buffer)
drm_device_write()
int drm_device_write(struct drm_node_s *handle,
int resource_id, unsigned in offset,
unsigned int size, void *buffer)
drm_getroot()
int drm_getroot(struct drm_node_s **root_handle)
drm_getchild()
int drm_getchild(struct drm_node_s *handle,
struct drm_node_s **child)
drm_getsibling()
int drm_getsibling(struct *handle,
struct drm_node_s **sibling)
drm_getparent()
int drm_getparent (struct drm_nodes_s *handle,
struct drm_node_s **parent)
drm_getnode()
int drm_getnode (struct drm_nodes_s *src,
struct drm_node_s **dest)
drm_setnode()
int drm_setnode (struct drm_nodes_s *handle)

Using DRM Facilities from Device Drivers

Device Identification

In the install() device driver entry point a driver attempts to connect to the device it intends to use. To locate its device, the driver needs to use the drm_get_handle() service routine. drm_get_handle() returns a pointer to the DRM node handle via its handle argument. The driver specifies the device it is interested in by using drm_get_handle() in the following manner:

install() {

  ret = drm_get_handle(buslayer_id,
  vendor_id, device_id, &handle)
  if(ret)
  {
    /* device not found .. abort installation */
  }
}

It is possible to supply a wild card to drm_get_handle() using
vendor_id = -1 and device_id = -1 as parameters. This claims and returns the first READY device in an unspecified search order. The driver examines the properties of the device to perform a selection. The driver needs to subsequently release the unused devices.

It is also possible to navigate the device tree using traversal functions and to obtain handles for the nodes. Device selection is performed by other modules, drivers or system management applications. If device selection has been done by some other means, the driver claims the device by using the drm_claim_handle() service routine, taking the node handle as a parameter.

install() {

  /* handle obtained externally */
  ret = drm_claim_handle(handle);
  if(ret)
  {
    /* Cannot claim device -- abort device install */
  }
..
}

The drm_free_handle() service routine is used to release the handle. The release of the device is typically done in the uninstall() routine of the driver. The drm_free_handle() takes the node handle to be freed as a parameter.

uninstall() {

  ret = drm_free_handle(handle);
  if(ret)
  {
    /* Error freeing handle, perhaps handle is bogus? */
  }
}

In Hot Swap environments, system management service routines select, make devices ready, and provide node handles for drivers to claim and use. The system management service routines facilitate the selection and dynamic loading of needed drivers and provides them with node handles for use.

Device Interrupt Management

DRM maintains all interrupt routing data for a device node. Drivers use the drm_register_isr() service routine to register an interrupt service routine and the drm_unregister_isr() service routine to clear a registration. Typically, this service routine is used in the install() and uninstall() entry points of the driver. To support sharing of interrupts in a hot swap/high availability environment, DRM internally dispatches all ISRs sharing an interrupt. The returned link_id is NULL, and the iointlink() kernel service routine does not perform any dispatches.

The following code segments illustrate the use of these DRM service routines:

install() {
  int ret, link_id;
  ret = drm_get_handle(buslayer_id, vendor_id,
  device_id, &handle);
  link_id = drm_register_isr(handle, isr_func, args);
}

uninstall() {
  ret = drm_unregister_isr(handle);
  if(Ret)
  {
    /* Cannot unregister isr? bogus handle? */
  }
  ret = drm_free_handle(handle);
  if(ret)
  {
    /* Cannot free handle, is handle bogus? */
  }
}

The interrupt management service routines return a status message when applied to a polled mode device.

Device Address Space Management

Many devices have internal resources that need to be mapped into the processor address space. The bus layers define such device-specific resources. For example, the configuration registers, the bus number, device number, and the function number of PCI devices are considered resources. The bus layer defines resource IDs to identify device-specific resources. Some of the device resources may need to be allocated. For example, the base address registers of a PCI device space need to be assigned a unique bus address space. DRM provides service routines to map and unmap a device resource into the processor address space. The function drm_map_resource() takes as parameters the device handle, resource ID and a pointer to store the returned virtual address. The drm_unmap_resource() takes as parameters a device handle and resource ID.

The following code fragment illustrates the use of these service routines:

install() {
  ret = drm_get_handle(PCI_BUSLAYER,SYMBIOS_VID,NCR825_ID,
                       &handle);
  if(ret)
  {
    /* Cannot find the scsi controller */
  }
  link_id = drm_register_isr(handle,scsi_isr,
                             scsi_isr_args);
  ret = drm_map_resource(handle,PCI_RESID_BAR1,
                         &scsi_vaddr);
  if(ret)
  {
    /* Bogus resource_id ? */
    /* resource not mappable */
    /* invalid device handle ? */
  }
  scsi_control_regs = (struct scsi_control *)(scsi_vaddr);
  ret =drm_unmap_resource(handle,PCI_RESID_BAR1);
  if(ret)
  {
    /* Bogus handle */
    /* resource is not mappable */
    /* resource was not mapped */
    /* invalid resource_id */
  }
}

Device I/O

DRM provides service routines to perform read and write to bus layer-defined resources. The drm_device_read() service routine allows the driver to read a device-specific resource. The drm_device_write() service routine allows the driver to perform a write operation to a device-specific resource. The resource IDs are usually specified in a bus layer-specific header file. For example, the file machine/pci_resource.h defines the PCIBUSLAYER resources. Both these service routines use the handle, resource ID, offset, size and a buffer as parameters. The meaning of the offset and size parameters is defined by the bus layer. Drivers implement platform-independent methods of accessing device resources by using these service routines. The following code fragment illustrates the use of these service routines.

#include <machine/pci_resource.h>
...

/* Enable PCI_IO_SPACE */
ret = drm_device_read(handle,PCI_RESID_CMDREG,0,0,
                      &pci_cmd);
if(ret)
{
  /* could not read device resource? validate parameters? */
}
pci_cmd |= PCI_IO_SPACE_ENABLE;
ret = drm_device_write(handle,PCI_RESID_CMDREG,0,0,
                       &pci_cmd);
if(ret)
{
  /* could not write device resource? validate parameters? */
}

This code is platform independent. The service routines take care of endian conversion, serialization, and other platform-specific operations.

DRM Tree Traversal

DRM provides a set of functions to navigate the device tree. Most of these functions take a reference node as input and provide a target node as output. The functions are listed below:

drm_getroot(&handle) returns the root of the device tree in handle.

drm_getparent(node,&handle) returns the parent of node in handle.

drm_getchild(node,&handle) returns the child of node in handle.

drm_getsibling(node,&handle) returns the sibling of node in handle.

Device Insertion/Removal

DRM provides two service routines that add nodes to the DRM tree: drm_locate() recursively finds and creates DRM nodes given a parent node as reference; drm_insertnode() inserts one node. The drm_insertnode() service routine is used when sufficient data is known about the device being inserted. The drm_locate() service routine is used to build entire subtrees.

A typical application involves inserting the bridge device corresponding to a slot, using the drm_insertnode() service routine. For a given configuration, the geographic data associated with the slots is generally known. This data is used to insert the bridge device. The data that is needed to insert a node is bus layer specific. For the PCIBUSLAYER, the PCI device number and function number are provided. The reference parent node determines the bus number of the node being inserted. Also, the bus layer determines the location of the inserted node in the DRM tree. Once the bridge is inserted, the drm_locate() service routine is used to recursively build the subtree below the bridge.

The drm_locate() and drm_insertnode() service routines initialize the DRM nodes to the IDLE state. The drm_selectnode() or drm_select_subtree() service routines are used to select the desired nodes and sets the nodes to the SELECTED state. The drm_alloc_resource() service routines are used to set the nodes to a READY state. DRM nodes in the READY state are available to be claimed by device drivers. After being claimed, the node is set to the ACTIVE state.

During extraction, device drivers release the DRM node using the drm_free_handle() service routine. This brings the DRM node back to a READY state. Resources associated with the nodes are released by using the drm_free_resource() service routine. This sets the nodes to the SELECTED state. The DRM nodes are then put in an IDLE state by using the drm_unselect_subtree() or drm_unselect_node() service routines. The IDLE nodes are removed by using the drm_delete_subtree(), or drm_delete_node() service routines. This last operation puts the device back into an unknown state. The device is now extracted from the system. A convenience function, drm_prune_subtree(), removes DRM's knowledge of an entire subtree. This routine operates on subtrees that are in the READY state.

When DRM nodes are inserted, they are time-stamped to assist in locating recently inserted nodes. Most of the DRM facilities are accessed by user mode programs using the sysctl() interface.

Using DRM Facilities from Applications

User mode applications use the sysctl() interface to get access to DRM facilities. It is possible to traverse the DRM tree, get node data, and to perform insertions and deletions using the sysctl() interface. The sys/drm_sysctl.h header file defines the MIB names and sysctl() data structures that are used. See the sysctl() man page for details on how to use the system call. The sysctl() call is invoked as:

(int) ret = sysctl(int *name, u_int namelen,
                   void *oldp, size_t *oldlenp,
                   void *newp, size_t newlen);

sysctl() parameters are described in the table below.

The top-level MIB name to access DRM-related information is CTL_HW. The second-level MIB name to access DRM information is HW_DRM. The third-level names provide DRM-specific facilities as described in the following table.

sysctl() Parameters and DRM-Specific Facilities  
DRM Facility
Description
DRM_GET_ROOT
This provides the sysctl() interface to the drm_getroot() service routine. The handle to the root of the DRM tree is returned in oldp. In the current implementation of DRM, the handle is returned as a
(void *) pointer with a size of 32 bits. A buffer that is sufficient to hold a (void *) pointer needs to be provided in oldp to hold the returned handle. For example, the sysctl() system call is made as follows:
{
  int mib[3];
  void *handle;
  int len;
  mib[0] = CTL_HW;
  mib[1] = HW_DRM;
  mib[2] = DRM_GET_ROOT;
  len = sizeof(handle);
  ret = sysctl(mib,3,&handle,
               &len, 0,0);
}
DRM_GET_PARENT
This provides the sysctl() interface to the drm_getparent() service routine. The fourth-level MIB name is set to the handle of the reference node for which a parent node is desired. The parent handle is returned in oldp in a manner similar to DRM_GET_ROOT. An example of the sysctl() system call follows:
{
  int mib[4];
  void *parent_handle;
  int len;
  mib[0] = CTL_HW;
  mib[1] = HW_DRM;
  mib[2] = DRM_GET_PARENT;
  mib[3] = handle;
  /* handle to the reference node */
  len = sizeof(parent_handle);
  ret = sysctl(mib,4,&parent_handle,
        &len, 0,0);
}
DRM_GET_CHILD
This provides the sysctl() interface to the drm_getchild() service routine. The fourth-level MIB name is set to the handle of the reference node. The child handle is returned in oldp in a manner similar to DRM_GET_PARENT.
DRM_GET_SIBLING
This provides the sysctl() interface to the drm_getsibling() service routine. The fourth-level name is set to the handle of the reference node. The sibling handle is returned in oldp in a manner similar to DRM_GET_PARENT.
DRM_GET_NODE
This call provides the DRM node data to the application. The fourth-level name is set to the handle of the reference node. The DRM node data is returned in a drm_sc_node structure given in oldp. Look for the definition of drm_sc_node in sys/drm_sysctl.h. A typical use of DRM_GET_NODE is as follows:
{
  int mib[4];
  struct drm_sc_node node;
  int len;mib[0] = CTL_HW;
  mib[1] = HW_DRM;
  mib[2] = DRM_GET_NODE;
  mib[3] = handle;  /* handle to the reference node */
  len = sizeof(node);
  /* get the data */
  ret = sysctl(mib,4,&node, &len, 0,0);
  printf("Vendor ID =
         %x\n",node.vendor_id);
  printf("Device ID = %x\n",
          node.device_id);
  printf(" Primary buslayer ID =
         %d\n",node.pbuslayer_id);
  printf(" Node type =
         %d\n",node.node_type);
  printf(" Node state = %d\n",node.state);
  if(node.node_type & DRM_BUS)
  {
    printf("Secondary buslayer_id
    d\n",node.sbuslayer_id);
  }
}

Hot Swap Management Applications

There are special facilities available for Hot Swap management applications. These facilities are specified as commands to the DRM_CMD third-level facility. The table below lists the commands that are available at this level.

Hot Swap Facilities  
Facility
Description
CMD_PROBE
Provides a sysctl() interface to the drm_locate() service routine. The fifth-level name provides a reference node for the probe.
CMD_SELECT
Provides the sysctl() interface for the drm_select_subtree() and drm_select_node() service routines.
CMD_ALLOC
Provides a sysctl() interface to the drm_alloc_resource() service routine.
CMD_PRUNE
Provides a sysctl() interface to the drm_prune_subtree() service routine.
CMD_UNSELECT
Provides a sysctl() interface to drm_unselect_subtree() and drm_unselect_node() service routines.
CMD_INSERT
Provides the sysctl() interface to the drm_insertnode() service routine.
CMD_FREE
drm_free resource service routine

The following code fragments illustrate how this interface is used.

do_probe(void *ref_node) {
  int mib[5];
  int retval;
  int len;
  int ret;
  mib[0] = CTL_HW;
  mib[1] = HW_DRM;
  mib[2] = DRM_CMD;
  mib[3] = CMD_PROBE;
  mib[4] = ref_node;
  len = sizeof(retval);
  /* perform the probe */
  ret = sysctl(mib,5,&retval,&len,0,0);
...
}

A sample of the CMD_INSERT code is shown below. The prop structure is filled in with bus layer-specific data that provides information on the node being inserted. The ref_node is the parent node of the node to be inserted.

do_insert(void *ref_node, void *prop, prop_len) {
  int mib[5];
  int handle;
  int len;
  int ret;
  mib[0] = CTL_HW;
  mib[1] = HW_DRM;
  mib[2] = DRM_CMD;
  mib[3] = CMD_INSERT;
  mib[4] = ref_node;
  len = sizeof(handle);
  /* perform the insert */
  ret = sysctl(mib,5,&handle,&len,prop,prop_len);
  ...
}

Example Driver


/* This is a sample driver for a hypothetical PCI device. This PCI device has a vendor_id of ABC_VENDORID and a device_id ABC_DEVICEID. This device has one base address register implemented as a PCI Memory BAR and needs 4K of space. The device registers are implemented in this space. The device needs a interrupt service routine to handle events raised by the device. It may be possible that there are multiple of these devices in the system. */


#include <pci_resource.h>

#define PCI_IO_ENABLE 0x1
#define PCI_MEM_ENABLE 0x2
#define PCI_BUSMASTER_ENABLE 0x4

struct device_registers {
unsigned int register1;
unsigned int register2;
unsigned int register3;
unsigned int register4;
};


struct device_static {
struct drm_node_s *handle;
struct device_register *regptr;
int bus_number;
int device_number;
int func_number;
};

abc_install(struct info_t *info)
{

struct device_static *static_ptr;
int rv = 0;
unsigned int val;

/* Allocate device static block */

static_ptr = (struct device_static *)
sysbrk(sizeof(struct device_static));


if(!static_ptr)
{
/* memory allocation failed !! */
goto error_0;
}

/* Find the device ABC_VENDORID, ABC_DEVICEID. Every call to abc_install() by the OS, installs a ABC device. The number of times abc_install() is called depends on how many static devices for ABC have been configured via the standard LynxOS device configuration facilities. This entry point is also called during a dynamic device install. */

/* A Hot Swap capable driver may replace the next call with drm_claim_handle() and pass the handle given by the system management layer, instead of finding the device by itself */

#if !defined(HOTSWAP)

rv = drm_get_handle(PCI_BUSLAYER,
ABC_VENDORID,ABC_DEVICEID,
&(static_ptr->handle));

#else /* Hot Swap capable */

rv = drm_claim_dhandle(info->handle);
static_ptr->handle = info->handle;

#endif

if(rv)
{
/* drm_get_handle or drm_claim_handle failed to find a
device. return failure to the OS saying install failed. */

debug(("failed to find device(%x,%x)\n",
ABC_VENDORID,ABC_DEVICEID));
goto error_1;
}

/* Register an interrupt service routine for this
device */

rv = drm_register_isr(static_ptr->handle,
abc_isr, NULL);

if(rv == SYSERR)
{

/*If register isr fails release the handle and exit*/

debug(("drm_register_isr failed %d\n",rv));
goto error_2;
}

/* Map in the memory base address register (BAR) */

rv = drm_map_resource(static_ptr->handle,
PCI_RESID_BAR0,
&(static_ptr->regptr));

if(rv)
{

/*drm_map_resource failed , release the device and
exit*/

debug(("drm_map_resource failed with %d\n",rv));

goto error_3;

}

/* Enable the device for memory access */

rv = drm_device_read(static_ptr->handle,
PCI_RESID_CMDREG,0,0,&val);


if(rv)
{
debug(("drm_device_read failed with %d\n",rv));
goto error_4;
}

val |= PCI_MEM_ENABLE ;

rv = drm_device_write(static_ptr->handle,
PCI_RESID_CMDREG,0,0,&val);

if(rv)
{
debug(("drm_device_write failed to update the
command register, error = %d\n",rv);
goto error_4;
}



/* Read the Geographic properties of the device, this
is used by the driver to uniquely identify the
device */

rv = drm_device_read(static_ptr->handle,
PCI_RESID_BUSNO,0,0,
&(static_ptr->bus_number));

if(rv)
{
debug(("drm_device_read failed to read bus
number %d\n",rv));
goto erro_4;
}

rv = drm_device_read(static_ptr->handle,
PCI_RESID_DEVNO,0,0,
&(static_ptr->device_number));

if(rv)
{
debug(("drm_device_read failed to read device
number %d\n",rv));
goto error_4;
}

rv = drm_device_read(static_ptr->handle,
PCI_RESID_FUNCNO,0,0,
&(static_ptr->func_number));

if(rv)
{

debug(("drm_device_read failed to read function
number %d\n",rv));
goto error_4 ;
}


/* perform any device specific initializations here,
the following statements are just illustrative */

/* recoset() is used to catch any bus errors */

if(!recoset())
{

static_ptr->regptr.register1 = 0;
static_ptr->regptr.register2 = 9600;
static_ptr->regptr.register3 = 1024;

if(static_ptr->regptr.register4 == 0x4)
{
static_ptr->regptr.register3 = 4096;
}
} else {
/* caught a bus error */
goto error_4;
}
noreco(); /* .................. and so on */

/* Succesfull exit from the install routine, return
the static pointer */

return(static_ptr);

error_4:

drm_unmap_resource(static_ptr->handle,
PCI_RESID_BAR0);

error_3:

drm_unregister_isr(static_ptr->handle);

error_2:

drm_free_handle(static_ptr->handle);

error_1:

sysfree(static_ptr,sizeof(struct device_static));

error_0:

return(SYSERR);

} /* abc_install */

abc_uninstall(struct device_static *static_ptr)
{

unsigned int val;
int rv = 0;

/* perform any device specific shutdowns */

static_ptr->regptr.register1 = 0xff ;

/* and so on */


/* Disable the device from responding to memory access */

rv = drm_device_read(static_ptr->handle,
PCI_RESID_CMDREG,0,0,&val);
if(rv)
{
debug(("failed to read device %d\n",rv));
}
val &= ~(PCI_MEM_ENABLE);
rv = drm_device_write(static_ptr->handle,
PCI_RESID_CMDREG,0,0,&val);
if(rv)
{
debug(("failed to write device %d\n",rv));
}


/* Unmap the memory resource */

rv = drm_unmap_resource(static_ptr->handle,
PCI_RESID_BAR0);
if(rv)
{
debug(("failed to unmap resource %d\n",rv));
}

/* unregister the isr */

rv = drm_unregister_isr(static_ptr->handle);
if(rv)
{
debug(("failed to unregister isr %d\n",rv));
}
/* release the device handle */

rv = drm_free_handle(static_ptr->handle);
if(rv)
{
debug(("Failed to free the device handle %d\n",
rv));
}

sysfree(static_ptr,sizeof(struct device_static));

return(0);
}

/* The other entry points of the driver are device specific */

abc_open(...) {
}

abc_read(...) {
}

abc_write(...) {
}

abc_ioctl(...) {
}

abc_close(...) {
}

abc_isr(...) {
}

Sample Application

/*
******************************************************
This program lists all boards in a Motorola 8216 chasis
******************************************************
*/

#include "errno.h"
#include <pci_resource.h>
#include "sys/sysctl.h"
#include "sys/drm_sysctl.h"
#include "sys/pci_sysctl.h"

#define TRUE 1
#define FALSE 0

#define OK TRUE
#define NOT_OK FALSE

#define DEV_NODE 8
#define BUS_NODE 4

#define MAX_SLOT 16
unsigned int root_node = 0;
unsigned int curr_node = 0;

struct drm_sc_node sc_node;
struct pci_sc_node pci_node;
struct pci_sc_busnode pci_busnode;
int mib[12];
int miblen;

int retval;
int cpx_slot;
struct drm_node_s *slot_handle;
struct drm_node_s *domainA_handle;
int slot_tbl[MAX_SLOT+1];

main()
{
int ret;
int dev_no;
unsigned int len;

printf("Slot# State\n");

/* Get the root node */

mib[0] = CTL_HW;
mib[1] = HW_DRM;
mib[2] = DRM_GET_ROOT;

len = sizeof(root_node);

ret = sysctl(mib,3,&root_node,&len,NULL,0);

if(ret)
{
perror("hsls-1:");
return(1);
}

curr_node = root_node;

/* Get the DomainA Bridge */

while(OK == get_next_node())
{
if(check_node(0x26,0x1011) == OK) break;
}

domainA_handle = (struct drm_node_s *)curr_node;

/* Init slot table */

for(cpx_slot = 1; cpx_slot < MAX_SLOT+1;
cpx_slot++)
{
slot_tbl[cpx_slot] = 0;
}

/* Get the child of the Domain Bridge */


ret =get_handle(domainA_handle,DRM_GET_CHILD,
&slot_handle);

/* Get all the siblings and populate the slot table */
/*
The slot number (index to slot_tbl) is derived from the
pci-device number from the following Motorola 8216
specific formula:
slot_number = 15 - pci_device_no ;
And depends on the wiring of the cPCI backplane.
*/

while(slot_handle)
{
dev_no = get_devno(slot_handle);
slot_tbl[15-dev_no] = (int)slot_handle;
ret = get_handle(slot_handle,DRM_GET_SIBLING,
&slot_handle);
if(ret == NOT_OK) break;
}

/* Display the slot table */

for(cpx_slot = 1; cpx_slot < MAX_SLOT+1; cpx_slot++) {
switch(cpx_slot) {
case 7:
case 9:
printf("%4.4d %s\n",cpx_slot,
"System Controller");
break;
case 8:
case 10:
printf("%4.4d %s\n",cpx_slot,
"Not Present");
break;
default:
if(slot_tbl[cpx_slot])
{
printf("%4.4d %s\n",cpx_slot,
"occupied");
} else {
printf("%4.4d %s\n",cpx_slot,
"empty");
}
break;
}
}
}

/* Traverse the DRM Tree */

int get_next_node() {

if(TRUE == has_child())
{
get_child();
return OK;
}

if(TRUE == has_sibling())
{
get_sibling();
return(OK);
}

while(TRUE == has_parent())
{
get_parent();
if(TRUE == has_sibling())
{
get_sibling();
return(OK);
}
}
return(NOT_OK);
}

/* Get a parent,child or sibling node */

int get_node(type)
int type;
{

int ret;
unsigned int node;
unsigned int len;


mib[0] = CTL_HW;
mib[1] = HW_DRM;
mib[2] = type;
mib[3] = curr_node;

len = sizeof(node);
ret = sysctl(mib,4,&node,&len,NULL,0);

if(ret)
{
perror("hsls-2:");
return(1);
}

return(node);

}

/* Check if currnode has children */

int has_child()
{
unsigned int node;
node = get_node(DRM_GET_CHILD);

if(!node || node == -1)
return(FALSE);
else
return(TRUE);
}

/* Get child of current node */

get_child()
{
return curr_node = get_node(DRM_GET_CHILD);
}

/* Check if curr node has sibling */

int has_sibling()
{
unsigned int node;
node = get_node(DRM_GET_SIBLING);
if(!node || node == -1)
return(FALSE);
else
return(TRUE);
}

/* Get sibling of current node */

get_sibling()
{
return curr_node = get_node(DRM_GET_SIBLING);
}

/* Check if current node has a parent */

int has_parent()
{
unsigned int node;
node = get_node(DRM_GET_PARENT);
if(!node || node == -1)
return(FALSE);
else
return(TRUE);
}

/* Get current node's parent */

get_parent()
{
return curr_node = get_node(DRM_GET_PARENT);
}

/* Check if current node matches given vendor,device id */


check_node(int dev,int vend)
{
int ret;
unsigned int len;


mib[0] = CTL_HW;
mib[1] = HW_DRM;
mib[2] = DRM_GET_NODE;
mib[3] = curr_node;

len = sizeof(sc_node);
ret = sysctl(mib,4,&sc_node,&len,NULL,0);

if(ret)
{
perror("hsls-3:");
return(1);
}


if((dev == sc_node.device_id) &&
(vend == sc_node.vendor_id)) return (OK);

return(NOT_OK);
}

/* Get the PCI device number given a node handle */

int get_devno(handle)
void *handle;
{
int ret;
unsigned int len;


mib[0] = CTL_HW;
mib[1] = HW_DRM;
mib[2] = DRM_GET_NODE;
mib[3] = (int)handle;

len = sizeof(sc_node);
ret = sysctl(mib,4,&sc_node,&len,NULL,0);

if(ret)
{
perror("hsls-4:");
return(NOT_OK);
}


if((sc_node.pbuslayer_id == PCI_BUSLAYER) &&
((sc_node.node_type & DEV_NODE) == DEV_NODE))
{
mib[0] = CTL_HW;
mib[1] = HW_DRM;
mib[2] = DRM_PCI;
mib[3] = PCI_GET_DEVNODE;
mib[4] = (int)handle;

len = sizeof(pci_node);
ret = sysctl(mib,5,&pci_node,&len,NULL,0);

if(ret)
{
perror("hsls-5:");
return(NOT_OK);
}

return(pci_node.device_no);
}

if (((sc_node.node_type & BUS_NODE) == BUS_NODE) &&
(sc_node.pbuslayer_id == PCI_BUSLAYER) &&
(sc_node.sbuslayer_id == PCI_BUSLAYER))
{
mib[0] = CTL_HW;
mib[1] = HW_DRM;
mib[2] = DRM_PCI;
mib[3] = PCI_GET_BUSNODE;
mib[4] = (int)handle;

len = sizeof(pci_busnode);
ret = sysctl(mib,5,&pci_busnode,&len,NULL,0);

if(ret)
{
perror("hsls-6:");
return(NOT_OK);
}

return(pci_busnode.device_no);
}

return(NOT_OK);
}

/* Given a reference handle, get the handle of it's parent,child or sibling */

int get_handle(handle,type,result)
void *handle;
int type;
void **result;
{

int ret;
unsigned int node;
unsigned int len;


mib[0] = CTL_HW;
mib[1] = HW_DRM;
mib[2] = type;
mib[3] = (int)handle;

len = sizeof(node);
ret = sysctl(mib,4,&node,&len,NULL,0);

if(ret)
{
perror("hsls-7:");
return(NOT_OK);
}

*result = (void *)node;
return(OK);

}



LynuxWorks, Inc.
855 Branham Lane East
San Jose, CA 95138
http://www.lynuxworks.com
1.800.255.5969
TOC PREV NEXT INDEX