Integrating a device driver with the Standard C Library

The eCos C library uses a table mechanism provided in cyg/devs/common/table.h to provide standard I/O functions. Each device is abstracted in this table, and each entry is of type struct Cyg_Device_Table_t as shown in Example 8-5:

Example 8-5. Device table for serial device driver


/*
 * Device table.
 */
struct Cyg_Device_Table_t
{
    char *              name;
    CYG_ADDRWORD        cookie;
//    cyg_off_t         seek_position;  // Not implemented yet

    /*
     * Note: The cookie is necessary for the function to differentiate 
     * between various instantiations of the same device type.
     *
     * Also note that open() will be called before the kernel scheduler
     * (and hence the interrupt system) starts, so it must be safe for this
     */
    Cyg_ErrNo           (*open) (CYG_ADDRWORD cookie, Cyg_DeviceOpenMode);
    Cyg_ErrNo           (*read_cancel) (CYG_ADDRWORD cookie, Cyg_IORB *);
    Cyg_ErrNo           (*write_cancel) (CYG_ADDRWORD cookie, Cyg_IORB *);
    Cyg_ErrNo           (*read_blocking) (CYG_ADDRWORD cookie, Cyg_IORB *);
    Cyg_ErrNo           (*write_blocking) (CYG_ADDRWORD cookie, Cyg_IORB *);
    Cyg_ErrNo           (*read_asynchronous) (CYG_ADDRWORD cookie, Cyg_IORB *);
    Cyg_ErrNo           (*write_asynchronous) (CYG_ADDRWORD cookie, Cyg_IORB *);
    Cyg_ErrNo		(*close) (CYG_ADDRWORD cookie);

    CYG_ADDRWORD	ioctl;
};
externC struct Cyg_Device_Table_t Cyg_Device_Table[]; 

To integrate a new device for use by the C library you need to modify the file in your source repository in devs/common/v1_1/src/table.cxx, and extend the table that is imlpemented there. Indeed this file provides the most obvious documentation to the methods to do this. The C library only uses the functions open(), read_blocking(), write_blocking() and close() from this table. If you do not wish to implement these, or other functions, simply fill in their entries in the table as NULL. For example, if you wanted to use a simple serial device for output only, you could have a table entry with all the function pointers NULL with the exception of write_blocking().

The best and simplest example from table.cxx is the implementation of a mock-up "haldiag" device. This provides an interface that the C library can use to the simple I/O routines provided by the eCos HAL. This is a slightly simplified version of the real version in table.cxx:


externC Cyg_ErrNo
Cyg_Device_Pseudo_Hal_Diag_open( CYG_ADDRWORD cookie,
                                 Cyg_DeviceOpenMode )
{
  HAL_DIAG_INIT();
  return ENOERR;
} 

externC Cyg_ErrNo
Cyg_Device_Pseudo_Hal_Diag_write_blocking( CYG_ADDRWORD cookie,
                                           Cyg_IORB *iorb )
{
  cyg_ucount32 i;

  for (i=0; i < iorb->buffer_length; i++) {
    HAL_DIAG_WRITE_CHAR(*((char *)iorb->buffer + i));
  } // for

  iorb->xferred_length = iorb->buffer_length;
  return ENOERR;
}

	

and its table entry would read:


{  "haldiag", (CYG_ADDRWORD) 0,
   &Cyg_Device_Pseudo_Hal_Diag_open,
   NULL,
   NULL,
   NULL,
   &Cyg_Device_Pseudo_Hal_Diag_write_blocking,
   NULL,
   NULL }, // no close
	

Note that the final entry in the table must be empty, i.e.


{ NULL, (CYG_ADDRWORD) 0, NULL, NULL, NULL, NULL } 

The device driver C API is implemented using the same "struct Cyg_Device_Table_t" types that are used in the table. The result is that the C API is just a set of macros:

Example 8-6. Implementation of C API with the table above


/*
 * C API is just a set of macros
 */
#define cyg_read_blocking(handle, iorb) \
    ((struct Cyg_Device_Table_t *)handle)->read_blocking( \
                      ((struct Cyg_Device_Table_t *)handle)->cookie, iorb)

#define cyg_write_blocking(handle, iorb) \
    ((struct Cyg_Device_Table_t *)handle)->write_blocking( \
                      ((struct Cyg_Device_Table_t *)handle)->cookie, iorb)

The handle parameter in these macros should be a device “cookie” as defined in table.cxx, which is simply the address of one of the table entries. They are exported to the user in table.h as constants. See table.h for a list of the available constants, commented “Handles to some of the device entries.”

Note: The cookie mentioned within the macro is a different thing altogether; the cookie in the structure is to allow more than one device to be handled by the same set of routines, via two such structures with distinct (Cyg_Device_Table_t) cookies.