ChorusOS 5.0 Board Support Package Developer's Guide

The Event Handler Function

This section describes the events handled by the event handler at the bus level.

The event handler is invoked by the parent bus/nexus driver when an event occurs. The event handler address is given to the parent bus/nexus driver when a connection is established between the bus driver and its parent bus/nexus driver.

The event handler may be called as an interrupt handler and therefore the event handler implementation must be restricted to the API allowed at interrupt level. Among all events which are mostly bus/nexus class specific, there are three shutdown related events (specified by the common bus API) which are discussed in this section:

SYS_SHUTDOWN

system (emergency) shutdown The SYS_SHUTDOWN event notifies the driver instance that the system is going to be shut down. The parent bus/nexus driver requires the child driver instance to perform an emergency shutdown of the device hardware.

DEV_SHUTDOWN

normal device shutdown The DEV_SHUTDOWN event notifies the driver instance that a normal device shutdown is requested by the bus/nexus driver.

DEV_REMOVAL

surprise device removalThe DEV_REMOVAL event notifies the driver instance that the associated device has been removed from the bus/nexus and therefore the driver instance has to be shutdown.


Note -

The omitted prefix ( DKI_ , BUS_ , ...) means that the event semantics are the same for all such events.


In general, the shutdown event processing goes through three phases:

  1. shutdown prolog

  2. shutdown mode

  3. shutdown epilog

The first phase (called shutdown prolog) is processed synchronously within the event handler. The main purpose of the shutdown prolog is to notify child drivers that the shutdown event has occurred (to propagate the shutdown prolog downstream, from parent to child). Once the shutdown event is processed by child drivers, the driver begins the shutdown epilog. The shutdown epilog is invoked when the last connection to the driver instance is closed. Between the shutdown prolog and epilog, the driver operates in a special mode (called shutdown mode). In shutdown mode, the driver accepts only a subset of operations from child drivers, allowing connections to the driver to be closed correctly.

The table below shows typical actions taken by the shutdown prolog depending on the event type:

 ActionSYS_SHUTDOWNDEV_SHUTDOWNDEV_REMOVAL
 notify child drivers (by calling children event handler) + + +
 abort operations in progress  - - +
 reset hardware  + - -

The SYS_SHUTDOWN prolog of a bus/nexus driver invokes the event handlers of child drivers connected to it. Once the invocation is done, the bus/nexus driver puts the hardware into a clean state.

Note that the SYS_SHUTDOWN event is processed synchronously, (that is, within the event handler). The only purpose of the SYS_SHUTDOWN event is to put the board hardware into a clean state to perform the system reboot (or restart) correctly.

The DEV_SHUTDOWN prolog notifies child drivers that the DEV_SHUTDOWN event has occurred. The actual device shutdown is deferred until the DEV_SHUTDOWN epilog.

The DEV_REMOVAL prolog is closed to the DEV_SHUTDOWN prolog. In addition, the DEV_REMOVAL prolog aborts all I/O operations in progress, because otherwise these operations will never be completed. Aborted operations return an error code to callers.

As soon as the shutdown prolog is processed, the driver changes its internal state to enter into shutdown mode. In shutdown mode, the driver accepts only a subset of operations from child drivers:

All other operations (like opening a new connection, or starting an I/O operation) are refused by the driver. When in shutdown mode, the driver must wait until a shutdown epilog condition is met. The shutdown epilog condition is met when the last connection to the driver's instance is closed. Because the shutdown epilog is processed in the DKI thread context, it can call all services and avoid any synchronization issues, even DKI services which can only be called in the DKI thread.

The following table shows typical actions taken by the shutdown epilog by event type.

 ActionDEV_SHUTDOWNDEV_REMOVAL
 reset hardware + -
 release system resources + +
 close connection to the parent driver  + +

The DEV_SHUTDOWN epilog puts hardware into a clean state, releases system resources used by the driver instance (io_unmap, mem_unmap, ...) and, finally, closes connection to the parent driver (close).

When a shutdown epilog closes the last connection to the parent driver, the shutdown epilog condition may be met in the parent driver too. In such a way, the shutdown epilog is propagated upstream (from child to parent). Note that if one of the child drivers does not shut down properly, the driver may get lost in the shutdown mode forever, and never meet the shutdown epilog condition.

In the following example of a PCI bus events handler of a PCI-to-ISA bridge driver, the W83C553_DEV_REMOVAL compilation flag is used to allow downsizing of the driver component at compile time in case the device removal mechanism is not needed.

    /*
     * The event handler is invoked by the parent driver (i.e. PCI bus) 
     * when an event occurs.
     *
     * The W83C553 driver always supports the PCI_SYS_SHUTDOWN and
     * PCI_DEV_SHUTDOWN events. The PCI_DEV_REMOVAL support is optional and
     * is provided only when W83C553_DEV_REMOVAL is defined.
     */ 
    static KnError
pciEventHandler (void* cookie, PciBusEvent event, void* arg)
{
    KnError      res     = K_OK;
    W83c553Data* w83c553 = (W83c553Data*)cookie;
    IsaDev*      isaDev;

    switch (event) {
        /*
         * Mask all ISA interrupts.
         * Then propagate the event to connected device drivers
         * and disconnect the controller from parent bus.
         */
    case PCI_SYS_SHUTDOWN: {
        w83c553->pciIntrOps->mask(w83c553->pciIntrId);
        isaDev = w83c553->dev;
        while (isaDev) {
            if (isaDev->evtHandler) {
                 isaDev->evtHandler(isaDev->cookie,
                      ISA_SYS_SHUTDOWN, arg);
            }
            isaDev = isaDev->next;
        }
        w83c553->pciConfOps->store_16(w83c553->pciConfId, PCI_COMMAND, 0);
        break;
    }
        /*
         * The normal device shutdown is processed only from the
         * normal mode. In other words, this event is
         * ignored if the driver already operates in the device
         * shut-down or removal mode.
         *
         * Here, we just flag that the device is entered into shutdown
         * mode and notify child drivers about it.
         * The real shutdown procedure will be done by the last call to
         * close().
         */
    case PCI_DEV_SHUTDOWN: {
        if (! w83c553->evtState) {
            w83c553->evtState = PCI_DEV_SHUTDOWN;
            isaDev = w83c553->dev;
					while (isaDev) {
						if (isaDev->evtHandler) {
							isaDev->evtHandler(isaDev->cookie, 
                        ISA_SYS_SHUTDOWN, arg);
						}
            	isaDev = isaDev->next;
        		 	}
            DKI_MSG(("%s: entered into shut-down mode\n", w83c553->path));
        }
        break;
    }
#if defined(W83C553_DEV_REMOVAL)
        /*
         * The device removal is processed from either the
         * normal mode or shutdown mode. In other words,
         * this event is ignored if the driver already operates in the
         * device removal mode.
         *
         * Here, we flag that the device is entered into removal
         * mode. In addition, the device ops (isa and bus) are
         * substituted to empty routines in order to avoid to access
         * the hardware which has been disappeared from the bus.
         * Once ops are substituted, we propagate the event to the
         * connected ISA drivers.
         * The real shutdown procedure will be done by the last call
         * to close().
         * removal() is called in order to abort any operation
         * in progess. 
         *
         * Note that, receiving ISA_DEV_REMOVAL, the driver client must
         * update pointers to the device service routines (ops) if they
         * have been previously copied by the client.
         */
    case PCI_DEV_REMOVAL: {
        if (w83c553->evtState != PCI_DEV_REMOVAL) {
            w83c553->evtState = PCI_DEV_REMOVAL;
            w83c553->isaOps->open           = (KnError(*)())downError;
            w83c553->isaOps->intr_attach    = (KnError(*)())downError;
            w83c553->isaOps->io_map         = (KnError(*)())downError;
            w83c553->isaOps->mem_map        = (KnError(*)())downError;
            w83c553->isaOps->dma_attach     = (KnError(*)())downError;
            w83c553->isaOps->resource_alloc = (KnError(*)())downError;
            w83c553->busOps->open           = (KnError(*)())downError;
            w83c553->busOps->intr_attach    = (KnError(*)())downError;
            w83c553->busOps->io_map         = (KnError(*)())downError;
            w83c553->busOps->mem_map        = (KnError(*)())downError;
            isaDev = w83c553->dev;
            while (isaDev) {
                isaDev->evtHandler(isaDev->cookie, ISA_DEV_REMOVAL, arg);
                isaDev = isaDev->next;
            }
            removal(w83c553);
            DKI_MSG(("%s: entered into removal mode\n", w83c553->path));
        }
        break;
    }
#endif
        /*
         * W83C553 does not drive the SERR# pin (p 55)
         * and don't care about palette snoop
         */
    default:
        res = K_ENOTIMP;
    }

    return res;
}

Hot-Plug Removal

The hot-plug removal event is typically reported via interrupts.

To be notified when the hot-plug removal occurs, the bus driver connects an interrupt handler to an appropriate interrupt source. Consider two typical mechanisms of hot-plug removal:

surprise removal

Surprise removal means a device can be removed at any time with no warning. For instance, PCMCIA is a surprise removal device.

non-surprise removal

Non-surprise removal means that the device cannot be removed until the system is prepared for it. For instance, Hotswap CompactPCI is a non-surprise removal device.

Surprise Removal

The surprise removal interrupt notifies the bus driver that a device has been removed from the bus. The bus driver interrupt handler usually detects the removed device (and associated device node) using bus specific status register(s).

Once the device is detected, the interrupt handler checks whether the device node is active. If the device node is inactive (there is no driver instance servicing the device), the only task of the bus driver is to update the device tree removing the device node. This frees all bus resources associated with the node .


Note -

The bus driver is not able to accomplish this task immediately at interrupt level because the services used are typically not available at interrupt level. These types of services can typically be called in the DKI thread context only.


To satisfy the invocation context requirements, the bus driver calls svDkiThreadTrigger requesting the DKI thread to invoke the removal procedure. Using the DKI thread also allows you to serialize all actions related to initialization and termination operations.

If the device node is active, the bus driver must shut down the corresponding device driver instance prior to invoking the removal procedure. To accomplish this task, the bus driver invokes the device driver event handler signaling the DEV_REMOVAL event. In fact, the bus driver performs the shutdown prolog for the given driver instance (see the "The Event Handler Function" section). In other words, the bus driver initiates the shutdown process for the given device sub-tree. (The removed device node is the root of the sub-tree.)

As the last action in the shutdown event process, the child device driver closes the connection to the bus driver and, at this moment, the bus driver performs the removal procedure. Note that the removal procedure is executed in the DKI thread context because the close service routine is called in the DKI thread context.

Non-Surprise Removal

The non-surprise removal interrupt requests the bus driver to enable the device removal from the bus. This is discussed at length above (in the "Surprise Removal" section). The difference between surprise and non-surprise removal is that in non-surprise removal, the bus driver requests the normal device shutdown service (DEV_SHUTDOWN) rather than the device removal service ( DEV_REMOVAL).

In addition, once the device tree is updated, the bus driver enables the device removal. Device removal enabling is usually signaled by an LED, and/or by a card ejection mechanism.