ChorusOS 4.0 Device Driver Framework Guide

Write Device Driver-Class-Specific Functions

In this next step, you will write an implementation of the services specific to the driver device class for a hardware device of the given class.

Once the code is written, these functions must be provided (made available) to the device driver's clients.


Note -

None of these functions are directly exported, but all of them are defined as static functions, and then grouped as indirect calls in an "operations data structure" (typed by the device class API). The device driver component then provides this "operations data structure" as a property when registering an instance of itself at initialization time.

This way of providing the device driver operations allows for a dynamic binding mechanism between device drivers and driver's clients.


Each device class API is different. Thus, the functions to write are different for different classes of device API. The complete list of the currently defined device class APIs may be found in the ddi(9) man page.

The following code example illustrates this step for an NS16x50 compatible UART device driver. In this example, the provided device class API is for the UART device class.


Note -

For clarity, only the code pertinent to this explanation is presented. Please refer to the complete implementation file for more details.


The NS16_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.

    /*
     * Open device.
     */
    static int
ns16_open (UartId id, ...) { ... }
    /*
     * Disable device interrupts.
     */
    static void
ns16_mask (UartId id) { ... }
    /*
     * Enable device interrupts.
     */
    static void
ns16_unmask (UartId id) { ... }
    /*
     * Send a buffer.
     */
    static void
ns16_transmit (UartId id, ...) { ... }
    /*
     * Abort an output in progress.
     */
    static void
ns16_abort (UartId id) { ... }
    /*
     * Send a break
     */
    static void
ns16_txbreak (UartId id) { ... }
    /*
     * Set/reset the modem control signals.
     */
    static void
ns16_control (UartId id, ...) { ... }
    /*
     * Set receive buffer.
     */
    static void
ns16_rxbuffer (UartId id, ...) { ... }
    /*
     * Close device.
     */     
    static void
ns16_close (UartId id) { ... }
    /*
     * NS16550 service routines:
     */
static UartDevOps ns16_ops =
{
    UART_VERSION_INITIAL,
    ns16_open,
    ns16_mask,
    ns16_unmask,
    ns16_transmit,
    ns16_abort,
    ns16_txbreak,
    ns16_control,
    ns16_rxbuffer,
    ns16_close
};
    /*
     * Init the NS16x50 uart. Called by BUS driver.
     */
    static void
ns16_init (DevNode node, void* pOps, void* pId)
{
    BusOps*       busOps = (BusOps*)pOps;
    Ns16_Device*  dev;
    ...
        /*
         * Allocate the device descriptor
         * (i.e. the driver instance local data)
         */
    dev = (Ns16_Device*)svMemAlloc(sizeof(Ns16_Device));
    ...
    dev->entry.dev_class = UART_CLASS;
    dev->entry.dev_id    = dev;
    dev->entry.dev_node  = node;
    ...
#if defined(NS16_DEV_REMOVAL)
    bcopy(&ns16_ops, &(dev->devOps), sizeof(ns16_ops));
    dev->entry.dev_ops = &(dev->devOps);
#else
    dev->entry.dev_ops = &ns16_ops
#endif
        /*
         * Allocate the device driver instance descriptor in the
         * device registry.
         * Note that the descriptor is allocated in an invalid state
         * and it is not visible for clients until svDeviceRegister()
         * is invoked.
         * On the other hand, the allocated device entry allows the
         * event handler (ns16_event) to invoke svDeviceEvent() on it.
         * If svDeviceEvent() is called on an invalid device entry,
         * the shutdown processing is deferred until svDeviceRegister().
         * In other words, if a shutdown event occurs during the
         * initialization phase, the event processing will be deferred
         * until the initialization is done.
         */
    dev->regId = svDeviceAlloc(&(dev->entry), 
                               UART_VERSION_INITIAL,
                               FALSE, 
                               ns16_release);
    ...
        /*
         * Finally, we register the new device driver instance
         * in the device registry. In case when a shutdown event
         * has been signaled during the initialization, the device entry
         * remains invalid and the ns16_release() handler is invoked
         * to shutdown the device driver instance. Otherwise, the device
         * entry becames valid and therefore visible for driver clients.
         */
    svDeviceRegister(dev->regId);

    DKI_MSG(("%s: %s driver started\n", dpath, NS16_DRV_NAME));
}