- 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.
- ErlDrvData (*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 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 port. If drv_data
is a pointer to memory allocated in start
, then stop
is the place to deallocate that memory.
- void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT 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.)
On Enea OSE the event
is one or more signals that can be retrieved using erl_drv_ose_get_signal
.
To use this with threads and asynchronous routines, create a pipe on unix, an Event on Windows or a unique signal number on Enea OSE. When the routine completes, write to the pipe (use SetEvent
on Windows or send a message to the emulator process on Enea OSE), this will make the emulator call ready_input
or ready_output
.
Spurious events may happen. That is, calls to ready_input
or ready_output
even though no real events are signaled. In reality it should be rare (and OS dependant), but a robust driver must nevertheless be able to handle such cases.
- 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 reserved for the emulator's internal use. The emulator will modify this field; therefore, it is important that the driver_entry
isn't declared const
.
- ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT 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, 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 default buffer of rlen
bytes, which can be used to return data. Data is returned different depending on the port control flags (those that are set with set_port_control_flags
).
If the flag is set to PORT_CONTROL_FLAG_BINARY
, a binary will be returned. Small binaries can be returned by writing the raw data into the default buffer. A binary can also be returned by setting *rbuf
to point to a binary allocated with driver_alloc_binary
. This binary will be freed automatically after control
has returned. The driver can retain the binary for read only access with driver_binary_inc_refc
to be freed later with driver_free_binary
. It is never allowed to alter the binary after control
has returned. If *rbuf
is set to NULL, an empty list will be returned.
If the flag is set to 0
, data is returned as a list of integers. Either use the default buffer or set *rbuf
to point to a larger buffer allocated with driver_alloc
. The buffer will be freed automatically after control
has returned.
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 activated 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).
- ErlDrvSSizeT (*call)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT 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
.
- void (*event)(ErlDrvData drv_data, ErlDrvEvent event, ErlDrvEventData event_data)
-
Intentionally left undocumented.
- int extended_marker
-
This field should either be equal to ERL_DRV_EXTENDED_MARKER
or 0
. An old driver (not aware of the extended driver interface) should set this field to 0
. If this field is equal to 0
, all the fields following this field also have to be 0
, or NULL
in case it is a pointer field.
- int major_version
-
This field should equal ERL_DRV_EXTENDED_MAJOR_VERSION
if the extended_marker
field equals ERL_DRV_EXTENDED_MARKER
.
- int minor_version
-
This field should equal ERL_DRV_EXTENDED_MINOR_VERSION
if the extended_marker
field equals ERL_DRV_EXTENDED_MARKER
.
- int driver_flags
-
This field is used to pass driver capability and other information to the runtime system. If the extended_marker
field equals ERL_DRV_EXTENDED_MARKER
, it should contain 0
or driver flags (ERL_DRV_FLAG_*
) ored bitwise. Currently the following driver flags exist:
ERL_DRV_FLAG_USE_PORT_LOCKING
- The runtime system will use port level locking on all ports executing this driver instead of driver level locking when the driver is run in a runtime system with SMP support. For more information see the
erl_driver
documentation. ERL_DRV_FLAG_SOFT_BUSY
- Marks that driver instances can handle being called in the
output
and/or outputv
callbacks even though a driver instance has marked itself as busy (see set_busy_port()
). Since erts version 5.7.4 this flag is required for drivers used by the Erlang distribution (the behaviour has always been required by drivers used by the distribution). ERL_DRV_FLAG_NO_BUSY_MSGQ
- Disable busy port message queue functionality. For more information, see the documentation of the
erl_drv_busy_msgq_limits()
function.
- void *handle2
-
This field is reserved for the emulator's internal use. The emulator will modify this field; therefore, it is important that the driver_entry
isn't declared const
.
- void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor)
-
This callback is called when a monitored process exits. The drv_data
is the data associated with the port for which the process is monitored (using driver_monitor_process
) and the monitor
corresponds to the ErlDrvMonitor
structure filled in when creating the monitor. The driver interface function driver_get_monitored_process
can be used to retrieve the process id of the exiting process as an ErlDrvTermData
.
- void (*stop_select)(ErlDrvEvent event, void* reserved)
-
This function is called on behalf of driver_select
when it is safe to close an event object.
A typical implementation on Unix is to do close((int)event)
.
Argument reserved
is intended for future use and should be ignored.
In contrast to most of the other call-back functions, stop_select
is called independent of any port. No ErlDrvData
argument is passed to the function. No driver lock or port lock is guaranteed to be held. The port that called driver_select
might even be closed at the time stop_select
is called. But it could also be the case that stop_select
is called directly by driver_select
.
It is not allowed to call any functions in the driver API
from stop_select
. This strict limitation is due to the volatile context that stop_select
may be called.