[erlang-questions] Question on Erlang Port Drivers

Vance Shipley <>
Thu Jan 26 18:10:12 CET 2012


On Thu, Jan 26, 2012 at 03:49:26AM -0500, David Drew wrote:
}  How would this example (the complex5 module and the port_driver.c file)
}  be expanded if you wanted to send/receive doubles instead of integers 

David,

The example sends data to the port, as a message, and receives a 
message back from the port.  It opens the port in normal mode so
the data sent in is a list of integers.  In this simple example
a list of two integers is sent as [Command, Argument].  On the
driver side it matches Command with the function you want to call.
It gets the result of the function and calls driver_output() to
send it as message from the port.

Changing Argument to a double could be done by lengthening the list
to handle the size of a double.  Something like [Command, Arg0, Arg1,
Arg2, Arg3, Arg4, Arg5, Arg6, Arg7], and you'd need to handle mapping
the eight bytes from a double.  But that would all be messy and not
portable.

Using port_command/2, or 'Port ! {PortOwner, {command, Data}}', is
the best solution for a driver which is handling a stream of data
or transactions where you can have a flying window.  For synchonous
calls there are other alternatives.

I have attached a version which uses erlang:port_call/3 to make a
synchronous call to the driver.  This method uses the Erlang external
term format for the messages it sends and receives.  In this way you
can easily marshall any type between the driver and your Erlang processes.

-- 
	-Vance
-------------- next part --------------
/* complex.c */

int foo(int x) {
  return x+1;
}

int bar(int y) {
  return y*2;
}

double foz(double x) {
	return x/1.0;
}

double baz(double y) {
	return y/2.0;
}
-------------- next part --------------
/*  ~/lib/erlang/doc/tutorial/c_portdriver.html  */

/* port_driver.c */

#include <stdio.h>
#include "erl_driver.h"

double foz(double);
double baz(double);

typedef struct {
    ErlDrvPort port;
} example_data;

static ErlDrvData example_drv_start(ErlDrvPort port, char *buff)
{
    example_data* d = (example_data*)driver_alloc(sizeof(example_data));
    d->port = port;
    return (ErlDrvData)d;
}

static void example_drv_stop(ErlDrvData handle)
{
    driver_free((char*)handle);
}

static void example_drv_output(ErlDrvData handle, char *buff, ErlDrvSizeT bufflen)
{
    example_data* d = (example_data*)handle;
    char fn = buff[0], arg = buff[1], res;
    if (fn == 1) {
      res = foo(arg);
    } else if (fn == 2) {
      res = bar(arg);
    }
    driver_output(d->port, &res, 1);
}

static ErlDrvSSizeT
example_drv_call(ErlDrvData handle, unsigned int command,
        char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen,
        unsigned int *flags)
{
    example_data* d = (example_data*)handle;
    double arg, res;
    int version, index = 0;

    if (ei_decode_version(buf, &index, &version))
        return((ErlDrvSSizeT) ERL_DRV_ERROR_GENERAL);
    if (ei_decode_double(buf, &index, &arg))
        return((ErlDrvSSizeT) ERL_DRV_ERROR_BADARG);
    switch (command) {
        case 3:
            res = foz(arg);
            break;
        case 4:
            res = baz(arg);
            break;
        default:
            return((ErlDrvSSizeT) ERL_DRV_ERROR_BADARG);
    }
    index = 0;
    if (ei_encode_version(*rbuf, &index)
            || ei_encode_double(*rbuf, &index, res))
        return((ErlDrvSSizeT) ERL_DRV_ERROR_ERRNO);
    else
        return((ErlDrvSSizeT) index);
}

ErlDrvEntry example_driver_entry = {
    NULL,			/* F_PTR init, N/A */
    example_drv_start,		/* L_PTR start, called when port is opened */
    example_drv_stop,		/* F_PTR stop, called when port is closed */
    example_drv_output,		/* F_PTR output, called when erlang has sent */
    NULL,			/* F_PTR ready_input, called when input descriptor ready */
    NULL,			/* F_PTR ready_output, called when output descriptor ready */
    "example_drv",		/* char *driver_name, the argument to open_port */
    NULL,			/* F_PTR finish, called when unloaded */
    NULL,			/* F_PTR control, port_command callback */
    NULL,			/* F_PTR timeout, reserved */
    NULL,		/* F_PTR outputv, reserved */
    NULL,
    NULL,
    NULL,
    example_drv_call,
    NULL,
    ERL_DRV_EXTENDED_MARKER,
    ERL_DRV_EXTENDED_MAJOR_VERSION,
    ERL_DRV_EXTENDED_MINOR_VERSION,
    0,
    NULL,
    NULL,
    NULL
};

DRIVER_INIT(example_drv) /* must match name in driver_entry */
{
    return &example_driver_entry;
}



More information about the erlang-questions mailing list