Home | Previous Page | Next Page   Appendix H. Custom Drivers > Driver Example >

Driver Example

The custom driver in this example takes over the open, close, read, and write responsibility. The example illustrates the form of a driver and the necessary initialization, registration, and mechanism of the driver. The coding of user- specific functionality is not represented.

The plcstdrv.c File

Assume that you chose MYDRIVER as the driver name and that you added this name to the onpload database with ipload. The plcstdrv.c file is as follows:

#include "plcstdrv.h"

int         DrConfig();

(*pl_get_user_method(driver, method)) ()
    char       *driver;
    int         method;
{
    if (strcmp(driver, "customdrv") == 0)
        {
        if (method == PL_MTH_CONFIGURE)
            return (DrConfig);
        }

    return (0);
}

Custom-Driver Code for MYDRIVER

The following driver code supports MYDRIVER:

#include <plcstdrv.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/fcntl.h>

extern char *malloc();
extern       errno;
 
static int DrOpen();
static int DrRead();
static int DrWrite();
static int DrClose();

#define CTRLDELIM 0x01               /* fake delimiter (CTRL-A)     */
#define REALDELIM 0x7c               /* real delimiter (|)          */
#define ESCAPSIGN 0x5c               /* escape sign    (\)          */
#define ENDRECORD 0x0a               /* end of record  (\n)         */

int     fd;

/*---------------------------------------------------------------------
 * DrConfig()
 *
 * Input  : (char *)        driver name
 *          (void *)        method table
 *
 * Return : PL_RTN_OK
 *
 * Schema : Fills in the driver table 
 *-------------------------------------------------------------------*/

int
DrConfig(driver,methodtable)
char           *driver;
void           *methodtable;
{
    pl_inherit_methods("Delimited", methodtable);
    pl_set_method_function(methodtable, PL_MTH_OPEN, DrOpen);
    pl_set_method_function(methodtable, PL_MTH_RAWREAD, DrRead);
    pl_set_method_function(methodtable, PL_MTH_RAWWRITE, DrWrite);
    pl_set_method_function(methodtable, PL_MTH_CLOSE, DrClose);
    pl_lock_globals();

    return PL_RTN_OK;
}

/*--------------------------------------------------------------------
 * DrOpen()
 *
 * Input  : (devicearray *) dev    device array structure
 *
 * Return : PL_RTN_FAIL            error
 *          PL_RTN_OK              open succeeded
 *
 * Schema : Open the specific file for that driver thread
 *          Note that the custom driver thread is bound to its
 *          own CPU VP, therefore it is safe to have globals like fd
 *------------------------------------------------------------------*/

static int
DrOpen(dev)
devicearray        *dev;
{
    fd = open(dev->filename, O_RDONLY);
    if (fd < 0)
        {
        return PL_RTN_FAIL;
        }

    return PL_RTN_OK;
}



/*--------------------------------------------------------------------
 * DrRead() 
 *
 * Input  : (char *) bf       output buffer to write record to
 *          (int)    size     size of output buffer
 *          (int *)  count    number of bytes written to output buffer
 *
 * Return : PL_RTN_FAIL       error
 *          PL_RTN_OK         returning buffer
 *          PL_RTN_EOF        returning the last buffer, no more data
 *
 *
 * Schema : Reads from input and fill up data buffer provided by the
 *          caller. Here, the caller expect a record where the delimiter
 *          is |. Our custom driver changes all CTRL-A into | and
 *          escapes the already existing | from input.
 *------------------------------------------------------------------*/

static int
DrRead(bf, size, count)
char               *bf;
int                 size;
int                *count;
{
    int             rtn;               /* return value              */
    int             n;                 /* bytes read in             */
    static char    *bftemp = 0;        /* temp buffer               */
    char           *p;                 /* pointer to temp buff      */
    char           *start;             /* start of output buffer    */
    static off_t    currseek = 0;      /* current seek in input     */
    int             escaped = 0;       /* did we escape last character */

    start = bf;

    if (bftemp == 0)
        {
        if ( (bftemp = malloc(size)) == 0 )
            {
            return PL_RTN_FAIL;
            }
        }
        

    /*
     * read data in
     */
    errno = 0;
    do
        {
        n = read(fd, bftemp, size);
        } while (n == -1 && errno == 4);

    rtn = (n < 0) ? PL_RTN_FAIL : (n == size) ? PL_RTN_OK : PL_RTN_EOF;

    currseek += n;

    p = bftemp;

    /*
     * format output buffer
     */
    while (size)
        {
        if (*p == REALDELIM)
            { 
            *bf = ESCAPSIGN; 
            escaped = 1;
            }
        else if (*p == CTRLDELIM)
            {
            *bf = REALDELIM;
            }
        else
            {
            *bf = *p;
            }

        size--;
        bf++;

        if (escaped && size)
            {
            *bf++ = *p;
            escaped = 0;
            size--;
            }
                
        p++;
  
        if ((int) (p - bftemp) == n)
            break;
        }

    if (escaped)
        {
        p--;
        rtn = PL_RTN_OK;
        }

    if ((int) (p - bftemp) != n)
        {
        currseek -= (off_t) (n - (p - bftemp));
        lseek(fd, currseek, SEEK_SET);
        }

    if (rtn == PL_RTN_EOF)
        {
        *bf = 0;
        }

    *count = (int) (bf - start);

    return rtn;
}


static int
DrWrite(bf, size)
char        *bf;
int          size;
{
    return PL_RTN_OK;
}


static int
DrClose(device)
devicearray *device;
{
    close(fd);

    return PL_RTN_OK;
}
Home | [ Top of Page | Previous Page | Next Page | Contents | Index ]