Simplest Linked-In Driver EVAH
Hypothetical Labs - kevin - March 24, 2009I’ve been tinkering around with linked-in drivers lately. There are several interesting projects using linked-in drivers so I thought I should learn how to write one.
I quickly discovered a dearth of simple examples on the web. All of the examples I found were either poorly documented or fairly complicated. I really wanted to see a basic “hello, world” style driver so I could see the “flow” of a driver. The closest I came to a minimal example was Cliff Moon’s cherly project.
So, in the finest open source tradition, I’ve taken Cliff’s project and cut it down to its absolute essentials. What’s left is a very basic driver which receives data via port_command/2 and echoes the received data back via driver_output_term().
The C side of the driver is contained in a single source file, basic_driver.c. The structure of the driver is straightforward. First, the driver includes the required header files, declares a struct to contain the driver’s state and exports functions required by erl_driver
#include <erl_driver.h>
#include <ei.h>
#include "config.h"
typedef struct _basic_drv_t {
ErlDrvPort port;
} basic_drv_t;
static ErlDrvData start(ErlDrvPort port, char* cmd);
static void stop(ErlDrvData handle);
static void process(ErlDrvData handle, ErlIOVec *ev);
Next, it defines a static instance of the ErlDrvEntry struct which is used by Erlang to interface with the driver. The driver initialization function is defined using the DRIVER_INIT macro.
static ErlDrvEntry basic_driver_entry = {
NULL, /* init */
start, /* startup */
stop, /* shutdown */
NULL, /* output */
NULL, /* ready_input */
NULL, /* ready_output */
"basic_drv", /* the name of the driver */
NULL, /* finish */
NULL, /* handle */
NULL, /* control */
NULL, /* timeout */
process, /* process */
NULL, /* ready_async */
NULL, /* flush */
NULL, /* call */
NULL, /* event */
ERL_DRV_EXTENDED_MARKER, /* ERL_DRV_EXTENDED_MARKER */
ERL_DRV_EXTENDED_MAJOR_VERSION, /* ERL_DRV_EXTENDED_MAJOR_VERSION */
ERL_DRV_EXTENDED_MAJOR_VERSION, /* ERL_DRV_EXTENDED_MINOR_VERSION */
ERL_DRV_FLAG_USE_PORT_LOCKING /* ERL_DRV_FLAGs */
};
DRIVER_INIT(basic_drv) {
return &basic_driver_entry;
}
Driver startup and teardown is handled by the start() and stop() functions. Since the driver doesn’t do very much, these functions merely allocate and free memory associated with the driver’s state.
static ErlDrvData start(ErlDrvPort port, char* cmd) {
basic_drv_t* retval = (basic_drv_t*) driver_alloc(sizeof(basic_drv_t));
retval->port = port;
return (ErlDrvData) retval;
}
static void stop(ErlDrvData handle) {
basic_drv_t* driver_data = (basic_drv_t*) handle;
driver_free(driver_data);
}
The real work is performed in process(). The function is called with the driver’s current state and a vector, or list, of binaries sent by the caller. The driver unbundles the user data from the vector, wraps it up in a tuple and send it back to the caller.
static void process(ErlDrvData handle, ErlIOVec *ev) {
basic_drv_t* driver_data = (basic_drv_t*) handle;
ErlDrvBinary* data = ev->binv[1];
ErlDrvTermData spec[] = {ERL_DRV_ATOM, driver_mk_atom("ok"),
ERL_DRV_BINARY, (ErlDrvTermData) data, data->orig_size, 0,
ERL_DRV_TUPLE, 2};
driver_output_term(driver_data->port, spec, sizeof(spec) / sizeof(spec[0]));
}
Finally, the driver is loaded and started from Erlang code in the Erlang module basic_driver.
-module(basic_driver).
-export([test/0, test/1]).
test(Message) when is_list(Message) ->
{ok, P} = load_driver(),
port_command(P, list_to_binary(Message)),
receive
Data ->
io:format("Data: ~p~n", [Data])
after 100 ->
io:format("Received nothing!~n")
end,
port_close(P).
test() ->
{ok, P} = load_driver(),
port_command(P, [<<"a">>, <<"b">>, <<"c">>]),
receive
Data ->
io:format("Data: ~p~n", [Data])
after 100 ->
io:format("Received nothing!~n")
end,
port_close(P).
%% Private functions
load_driver() ->
SearchDir = filename:join([filename:dirname(code:which(basic_driver)), "..", "priv"]),
case erl_ddll:load(SearchDir, "basic_drv") of
ok ->
{ok, open_port({spawn, 'basic_drv'}, [binary])};
Error ->
Error
end.
Source code for the basic_driver project is available here.
Categories: Blogs Hypothetical Labs
Comments
No comments so far, you could be the first.Add comment
Erlang on Twitter
» bagus_erlang (bagus): Marah aja kerjaan…
» jamescarr (jamescarr): Working my way through “Learn you some erlang for great good!” tonight. http://t.co/DDTJwDJO
» bagus_erlang (bagus): Capek bngt nyanyi (kemesraan)
» AngginiMaulani (Δnggini MF): Kenapa mamah jdi ngomongin erlang gini:/
» wolfeidau (Mark Wolfe): Submitted update to the #erlang package on #OpenWRT http://t.co/4Brn63XU if anyone wants to test it please ping me /cc @ErlangEmbedded
» ivansyahhsn (ivansyah): Iya dewa erlang hbd,awas ya siksamu menanti RT @indrasan: selamat ulang tahun saudara reza erlang @rezasur semoga makin banyak proyek nya ya
» CzarneckiD (David Czarnecki): It was like Mr. Toad’s Wild Open Source Ride here tonight: Erlang, Riak, CouchDB, Ruby and Python. #nofastpassrequired
» yang_yihming (Yiming Yang): @vw009 Which language do you often use in parallel programing? Ocaml? Erlang? C01? Or some other language?
» tengkushara (T Muni Fahtu Zahra): RT @fathiaamandaaa: RT @indrasan: selamat ulang tahun saudara reza erlang @rezasur semoga makin banyak proyek nya ya.
» wolfeidau (Mark Wolfe): Coding in emacs and enjoying it, hell has frozen over.. #erlang #emacs
Statistics
Number of aggregated posts: 10503
Number of comments: 2137
Most recent article: May 21, 2012
Latest comments
» Erno on 100,000 Lines of Assembly Language: Excellent posting. Undoubtedly you are an expert when it comes to this writing. This is absolutely the first time I…
» Jessica on 30 September 2011: Basho Technologies, Erlang Solutions and Trifork AS Announce Big Data and NoSQL R: yeah of course. I just thought that everything will be just alright and I want to have these kind of…
» DRS786 on 25 May 2012: Poznan Erlang User Group Event: I’m going!