[Ericsson AB]

driver_entry

MODULE

driver_entry

MODULE SUMMARY

The driver-entry structure used by erlang drivers.

DESCRIPTION

The driver_entry structure is a C struct that all erlang drivers defines. It contains entry points for the erlang driver that are called by the erlang emulator when erlang code accesses the driver.

The erl_driver driver API functions needs a port handle that identifies the driver instance (and the port in the emulator). This is only passed to the start function, but not to the other functions. The start function returns a driver-defined handle that is passed to the other functions. A common practice is to have the start function allocating some application-defined structure and stash the port handle in it, to use it later with the driver API functions.

The driver call-back functions are called synchronously from the erlang emulator. If they take too long before completing, they can cause timeouts in the emulator. Use the queue or asynchronous calls if nessecary, since the emulator must be responsive.

The driver structure contains the name of the driver and some 15 function pointers. These pointers are called at different times by the emulator. Here is the declaration of driver_entry:

The only exported function from the driver is driver_init. This function returns the driver_entry structure that points to the other functions in the driver. The driver_init function is declared with a macro DRIVER_INIT(drivername). (This is because different OS's have different names for it.)

When writing a driver in C++, the driver entry should be of "C" linkage. One way to do this is to put this line somewhere before the driver entry: extern "C" DRIVER_INIT(drivername);.

Note!

The design of SMP support for drivers is ongoing. There will probably be incompatible driver changes (only effecting drivers run on the runtime system with SMP support) released as patches for R11B.
    Potential incompatibility: Previously, specific driver call-backs were always called from the same thread. This is not true in the runtime system with SMP support. Calls to call-backs will be made from different threads, e.g., two consecutive calls to exactly the same call-back can be made from two different threads. This will in most cases not be a problem. All calls to call-backs are synchronized, i.e., only one call-back will be called at a time.
    In the future the default behavior will probably be the following: Calls to call-backs will, as now, be made from different threads. Calls to call-backs in the same driver instance will be synchronized. It will probably be possible to configure so that all calls to call-backs in all driver instances of a specific driver type will be synchronized. It may be possible to configure so that all calls to call-backs of a driver instance or a of a specific driver type will be made from the same thread.

typedef struct erl_drv_entry {
    int (*init)(void);          /* called at system start up for statically
                                   linked drivers, and after loading for
                                   dynamically loaded drivers */ 

#ifndef ERL_SYS_DRV
    ErlDrvData (*start)(ErlDrvPort port, char *command);
                                /* called when open_port/2 is invoked.
                                   return value -1 means failure. */
#else
    ErlDrvData (*start)(ErlDrvPort port, char *command, SysDriverOpts* opts);
                                /* special options, only for system driver */
#endif
    void (*stop)(ErlDrvData drv_data);
                                /* called when port is closed, and when the
                                   emulator is halted. */
    void (*output)(ErlDrvData drv_data, char *buf, int len);
                                /* called when we have output from erlang to 
                                   the port */
    void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event); 
                                /* called when we have input from one of 
                                   the driver's handles) */
    void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event);  
                                /* called when output is possible to one of 
                                   the driver's handles */
    char *driver_name;          /* name supplied as command 
                                   in open_port XXX ? */
    void (*finish)(void);        /* called before unloading the driver -
                                   DYNAMIC DRIVERS ONLY */
    void *handle;               /* not used -- here for backwards compatibility */
    int (*control)(ErlDrvData drv_data, unsigned int command, char *buf, 
                   int len, char **rbuf, int rlen); 
                                /* "ioctl" for drivers - invoked by 
                                   port_command/3) */
    void (*timeout)(ErlDrvData drv_data);       /* Handling of timeout in driver */
    void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev);
                                /* called when we have output from erlang
                                   to the port */
    void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data);
    void (*flush)(ErlDrvData drv_data);
                                /* called when the port is about to be 
                                   closed, and there is data in the 
                                   driver queue that needs to be flushed
                                   before 'stop' can be called */
    int (*call)(ErlDrvData drv_data, unsigned int command, char *buf, 
                   int len, char **rbuf, int rlen, unsigned int *flags); 
                                /* Works mostly like 'control', a syncronous
                                   call into the driver. */
    void (*event)(ErlDrvData drv_data, ErlDrvEvent event,
                  ErlDrvEventData event_data);
                                /* Called when an event selected by 
                                   driver_event() has occurred */
} ErlDrvEntry;
    

EXPORTS

int init(void)

This is called directly after the driver has been loaded by erl_ddll:load_driver/2. (Actually when the driver is added to the driver list.) The driver should return 0, or if the driver can't initialize, -1.

int start(ErlDrvPort port, char* command)

This is called when the driver is instantiated, when open_port/2 is called. The driver should return a number >= 0 or a pointer, or if the driver can't be started, one of three error codes should be returned:

ERL_DRV_ERROR_GENERAL - general error, no error code

ERL_DRV_ERROR_ERRNO - error with error code in erl_errno

ERL_DRV_ERROR_BADARG - error, badarg

If an error code is returned, the port isn't started.

void stop(ErlDrvData drv_data)

This is called when the port is closed, with port_close/1 or Port ! {self(), close}. Note that terminating the port owner process also closes the p port.

void output(ErlDrvData drv_data, char *buf, int len)

This is called when an erlang process has sent data to the port. The data is pointed to by buf, and is len bytes. Data is sent to the port with Port ! {self(), {command, Data}}, or with port_command/2. Depending on how the port was opened, it should be either a list of integers 0...255 or a binary. See open_port/3 and port_command/2.

void ready_input(ErlDrvData drv_data, ErlDrvEvent event)
void ready_output(ErlDrvData drv_data, ErlDrvEvent event)

This is called when a driver event (given in the event parameter) is signaled. This is used to help asynchronous drivers "wake up" when something happens.

On unix the event is a pipe or socket handle (or something that the select system call understands).

On Windows the event is an Event or Semaphore (or something that the WaitForMultipleObjects API function understands). (Some trickery in the emulator allows more than the built-in limit of 64 Events to be used.)

To use this with threads and asynchronous routines, create a pipe on unix and an Event on Windows. When the routine completes, write to the pipe (use SetEvent on Windows), this will make the emulator call ready_input or ready_output.

char *driver_name

This is the name of the driver, it must correspond to the atom used in open_port, and the name of the driver library file (without the extension).

void finish(void)

This function is called by the erl_ddll driver when the driver is unloaded. (It is only called in dynamic drivers.)

The driver is only unloaded as a result of calling unload_driver/1, or when the emulator halts.

void *handle

This field is not used, it's still around only for historical reasons. It should be NULL. Don't use it.

int control(ErlDrvData drv_data, unsigned int command, char *buf, int len, char **rbuf, int rlen)

This is a special routine invoked with the erlang function port_control/3. It works a little like an "ioctl" for erlang drivers. The data given to port_control/3 arrives in buf and len. The driver may send data back as a driver binary, using *rbuf and rlen.

This is the fastest way of calling a driver and get a response. It won't make any context switch in the erlang emulator, and requires no message passing. It is suitable for calling C function to get faster execution, when erlang is too slow.

If the driver wants to return data, it should return it in rbuf. When control is called, rbuf points to a pointer to a buffer of rlen bytes, which can be used to return data. Data is returned depending of the port control flags (those that are set with set_port_control_flags). If the flag is set to PORT_CONTROL_FLAG_BINARY, then rbuf should point to a driver binary or be NULL. Note that this binary must be freed. If rbuf is set to NULL, an empty list will be returned.

If the flag is set to 0, rbuf points to a char* containing data, that is returned as a list of integers. Using binaries is faster if more than a few bytes are returned.

The return value is the number of bytes returned in *rbuf.

void timeout(ErlDrvData drv_data)

This function is called any time after the driver's timer reaches 0. The timer is activeated with driver_set_timer. There are no priorities or ordering among drivers, so if several drivers time out at the same time, any one of them is called first.

void outputv(ErlDrvData drv_data, ErlIOVec *ev)

This function is called whenever the port is written to. If it is NULL, the output function is called instead. This function is faster than output, because it takes an ErlIOVec directly, which requires no copying of the data. The port should be in binary mode, see open_port/2.

The ErlIOVec contains both a SysIOVec, suitable for writev, and one or more binaries. If these binaries should be retained, when the driver returns from outputv, they can be queued (using driver_enq_bin for instance), or if they are kept in a static or global variable, the reference counter can be incremented.

void ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data)

This function is called after an asynchronous call has completed. The asynchronous call is started with driver_async. This function is called from the erlang emulator thread, as opposed to the asynchronous function, which is called in some thread (if multithreading is enabled).

int call(ErlDrvData drv_data, unsigned int command, char *buf, int len, char **rbuf, int rlen, unsigned int *flags)

This function is called from erlang:port_call/3. It works a lot like the control call-back, but uses the external term format for input and output.

command is an integer, obtained from the call from erlang (the second argument to erlang:port_call/3).

buf and len provide the arguments to the call (the third argument to erlang:port_call/3). They can be decoded using ei functions.

rbuf points to a return buffer, rlen bytes long. The return data should be a valid erlang term in the external (binary) format. This is converted to an erlang term and returned by erlang:port_call/3 to the caller. If more space than rlen bytes is needed to return data, *rbuf can be set to memory allocated with driver_alloc. This memory will be freed automatically after call has returned.

The return value is the number of bytes returned in *rbuf. If ERL_DRV_ERROR_GENERAL is returned (or in fact, anything < 0), erlang:port_call/3 will throw a BAD_ARG.

See Also

erl_driver(3), erl_ddll(3), kernel(3), erlang(3)

AUTHORS

Kenneth Lundin - support@erlang.ericsson.se
Jakob Cederlund - support@erlang.ericsson.se

erts 5.5.1
Copyright © 1991-2006 Ericsson AB