This page is available in: en

Downloads

We are currently preparing the source code related to this article for download. It will be available soon.

SDI Device Driver Guide – Part 1

Written by: Mukul Sharma and Fauzi Abbas, Centre for High Performance Embedded Systems, NTU Singapore
Edited by: Mohit Sindhwani (TE@Onghu)

The T-Kernel supports a number of models for device drivers. In order to make it easy for developers to write device drivers, the T-Engine Forum has released a document called T-Engine Device Driver Interface Library Specification and associated source code. One of the device driver models mentioned in the specification is called the Simple Device Driver Interface or SDI. In this article, we present the steps that are needed to create an SDI device driver for the T-Kernel.

For this example, we create a dummy device and a device driver to access it. Although this is a simplistic example, it demonstrates the basic workings of a simple driver and also points out the main steps involved in creating a device driver.

Some parts of this article are based on the presentation about device drivers available from TEADEC and includes other information based on our experience of writing a device driver.

Equipment and Tools

Our developing environment is set up using the following:

  • Red Hat Linux 9.0 or Ubuntu Linux 5.04 (Intel x86 versions) or Cygwin.
  • T-Engine Development Kit Release 1.11E or later.
  • T-Engine/SH7727 with 128MB Compact Flash

In this guide, references are made to the device driver source codes available on the T-Engine Development Kit Release while working on the current sample device drivers. These device drivers include the LCD, Console, Touch Panel and several other device drivers and they strictly follow the standard specifications provided by T-Engine Forum. These sample drivers provide a good way to learn how device drivers are written.

Background

The picture below shows the device driver stack that is used for creating a device driver.

T-Kernel Device Management Function (from T-Kernel Specification – TEF020–S001–01.00.00/en)

As can be seen, the device driver itself works with 3 main parts:

  1. The device driver must interface with the physical hardware that it controls
  2. It must work with the kernel so that the kernel knows when/ how to call it
  3. It must be accessible by applications/ middleware that require to use the device

Since Item #3 in the above list is highly hardware dependent, for the sake of this example, we try to create a simple device that does not actually work with physical hardware. Instead, it simply outputs messages to indicate when the various functions are called. When creating a real device driver, those lines should be replaced with the correct processing for controlling/ accessing/ using the device. The main focus of this article is how to use SDI to manage Items #1 and #2.

Typically, a simple device driver must provide at least the following functions:

  1. Open Device and Close Device
  2. Read from and Write to the device
  3. Event Function (used for events like suspend, resume, etc.)

Essential Reading: The following references are essential reading for this article:

  1. T-Kernel Specification: Section 5.3 – Device Management Functions: This explains the various functions that we use.
  2. T-Engine Device Driver Interface Library Specification: This explains SDI and related data structures, etc.

Creating the Device Driver

The main steps in creating a device driver are as follows:

  1. Create functions to open and close the device (if required)
  2. Create functions to read from and write to the device (if required)
  3. Create the event function to receive events (if required)
  4. Create a structure to describe your device
  5. Register your device with the T-Kernel

Step 1 – Functions to open and close the Device

These functions are called when an application tries to open or close the device. In the accompanying source code, refer to sample_device_driver/src/main.c, line 18 – 35.

The open function usually initializes the device while the close function handles the necessary cleanup before ‘free’ing the device. The open function is called during the tk_opn_dev() call and close function is called during the tk_cls_dev() call.

If a device is opened multiple times, open function is called the first time it is opened and close function the last time it is closed. If TDA_OPENREQ is designated as driver attribute, then open/close function will be called for all open/close operations even in case of multiple openings.

In the code below, the open function and close functions are shown for our sample device.


LOCAL ER openfn  ( ID devid, UINT omode, VP exinf)
{
    /* start device use */
    // Content is Device Dependent
    printf(" A call has been made for the Device Open Function.\n");
    return E_OK;
}

LOCAL ER closefn  ( ID devid, UINT option, VP exinf)
{
    /* terminate device use */
    // Content is Device Dependent
    printf(" A call has been made for the Device Close Function.\n");
    return E_OK;
}

ID devid    : Device ID of the device to open
UINT omode  : Open mode (same as tk opn dev)
VP exinf    : Extended information set at device registration
Return code : Error

For more information, refer to the following:

  • T-Kernel Specifications Chapter 5.3.4 – Device Driver Interface.
  • T-Kernel Specifications Chapter 5.3.3 – Device Registration.
  • T-Kernel Specifications Chapter 5.3.2 – Application Interface.

Step 2 – Functions to read and write

The next step is to declare the Read/Write Functions for the Device. These functions are required to process read or write requests to the device. Read and Write Functions follow a standard definition to serve the various Read/Write Requests done from the device. The functions will typically use a switch case statement format to serve different read/write requests.

In the accompanying source code, refer to sample_device_driver/src/main.c, line 40 – 85 for our read/ write functions. The read function is shown below and the write function is created in a similar fashion.


LOCAL    INT    readfn(ID devid, INT start, INT size, VP buf, SDI sdi)
{
    printf(" A call has been made for the Device Read Function.\n");

    switch (start)
    {
        case DN_DVCSPEC:    /* Developer defined Device Specifications Read Call */
                            // Content is Device Dependent                        
            printf(" This function gets the Device Specifications and returns them back to the application.\n");
            break;

        case DN_DVCREAD:    /* Developer defined Device Read Call */
                            // Content is Device Dependent
            printf(" This function gets the Device Specific Data and returns it back to the application.\n");
            break;

        default:
        printf(" This is default action for the read function call.\n");
            break;
    }
    return E_OK;
}

In general, start refers to the starting address from which data is to be read/ written. According to the specification, zero or positive values are used to read/ write device specific data and negative values are used to read/ write attribute information about the device. Attribute information that can be read/ written is device-dependent and for this, developers should define their own constants referring to the various attributes. This can be done by defining the constants in a header file (e.g. sample_drvr.h) and this header file should be copied to the include folder of the T-Engine environment (/te/include).

Read/ Write Functions are called during tk_rea_dev()/tk_srea_dev() or tk_wri_dev()/tk_swri_dev() calls.

Note that the order of arguments received by the Read/ Write Functions differs from the order in which these arguments are passed from application.

For more information, refer to the following:

  • T-Kernel Specifications Chapter 5.3.4 – Device Driver Interface.
  • T-Kernel Specifications Chapter 5.3.2 – Application Interface.

Step 3 – Event Function

A device driver can receive events from the system and must carry out processing related to these events. For physical devices, the driver may be issued requests to suspend or resume the devices. In the accompanying source code, refer to sample_device_driver/src/main.c, line 90 – 124 for our event function. Event requests are called when processing the tk_evt_dev() call.


LOCAL    INT    eventfn  (INT evttyp, VP evtinf, SDI sdi)
{
    INT    er;
    printf(" A call has been made for the Device Event Function.\n");
    switch (evttyp)
    {
        case    TDV_SUSPEND:    //  Predefined Suspend Call 
            if (suspended == FALSE)
            {
            // Developer defined function to set device in suspended mode
            printf(" This function will set the device in suspended state.\n");
            suspended = TRUE;
            }
            er = E_OK;
            break;

        case    TDV_RESUME:        //  Predefined Resume Call 
            if (suspended == TRUE)
            {
            // Developer defined function to resume the device from suspended mode
            printf(" This function will resume the device from the suspended state.\n");
            suspended = FALSE;
            }
            er = E_OK;
            break;
        default:
            er = E_ILUSE;
            break;
    }

    return er;
}

The constants used here (TDV_SUSPEND and TDV_RESUME) are defined by the Device Manager in the “tk/devmgr.h” header file. For more information, refer to:

  • T-Kernel Specifications Chapter 5.3.4 – Device Driver Interface.
  • T-Kernel Specifications Chapter 5.3.2 – Application Interface.
  • T-Kernel Specifications Chapter 5.3.6 – Device Event Notification

Step 4 – Driver Registration Structure

Once we have decided the functions that the device driver will support, it is necessary to package the functions and inform the kernel about the functions it call when dealing with the device. In registering the device, the first step is to create and populate the Device Registration Structure. This structure tells the T-Kernel what the device is called and also specifies the capabilities and attributes of the device. The structure shown below is based on SDI. In the attached source code, refer to sample_device_driver/src/main.c, line 134 – 146 to see where the device is defined.

In this structure, it is necessary to specify the functions that the device driver supports. An SDI device driver may support any of the following functions:

  • Open
  • Close
  • Read
  • Write
  • Event

Where the function is supported, the corresponding entry in the structure should store the address of the function in the driver. Where the function is not supported, the entry should be stored as NULL. Note that we used the names openfn, closefn, etc. for the functions in our device driver and are assigning these in the structure. However, these functions may be named to your choosing – just make sure that the correct names are assigned in the device structure below.


SDefDev    ddev = {
    NULL,       /* exinf: extended information */
    "DRVSAMP",    /* devnm: physical device name */
                /* devnm should be max. 8 char in the format:
                   "format type + unit + subunit" */
    0,            /* drvatr driver attribute */
    TDK_UNDEF,    /* devatr device attribute */    //Undefined Device Type
    0,        /* nsub: number of subunits */
    1,        /* blksz : block size for specific data */
    openfn,    /* open function*/
    closefn,    /* close function*/
    readfn,    /* read function*/
    writefn,    /* write function*/
    eventfn,    /* event function*/
};


For more details about the device registration structure, refer to:

  • T-Kernel Specification Chapter 5.3.3 – Device Registration
  • T-Engine Device Driver Interface Library Specification

Step 5 – Entry Routine for Device Driver

The final step in creating a device driver is to register the device with the T-Kernel. The structure above describes the features of the device and the functions that the driver supports. Finally, we need to create an entry routine for the device driver and register the device.

The entry routine of the device driver is called when the device driver is loaded. The entry routine may be used to perform device-specific initialization, if required. In addition, it must make a system call to the T-Kernel to define and register the device. In the attached source code, this is done in sample_device_driver/src/main.c, line 130 – 160.

This is a standard step for the device drivers and is done by a call to SDefDevice().


LOCAL SDI DvcSdi;    /* device driver I/F handle */ (Global Variable)
ER    er;
T_IDEV    idev;

er  =   SDefDevice (&ddev, &idev, &DvcSdi);

ddev    : Pointer to Device Registration Structure (Refer to Step 1)
idev    : Pointer to location of device initialization information (T-Kernel)
DvcSdi  : Pointer to location of driver interface access handler    
er      : Error code returned

For more detail, refer to:

  • T-Engine/SH7727 Development Kit Device Driver Manual Chapter 2.4.1 – Define Device.

In Conclusion

This article introduces how to write a device driver for the T-Kernel based on the Simple Device Driver Interface. The concepts for a General Device Driver Interface (GDI) are similar and the concepts here can be extended to apply to the GDI model.

In Part 2 of this article, we will look at some of the issues in packaging, building and loading the device driver. We will also look at a simple application to use this device driver.

We are currently packaging the source code for this article and it will be made available soon.