%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%% Dynamic Driver Loader and Linker
%%
%% Interface for dynamic library/shared object driver loader/linker.
%% Provides methods for loading, unloading and listing drivers.
-module(erl_ddll).
-export([load_driver/2, load/2,
unload_driver/1, unload/1, reload/2, reload_driver/2,
format_error/1,info/1,info/0, start/0, stop/0]).
%%----------------------------------------------------------------------------
-type path() :: string() | atom().
-type driver() :: string() | atom().
%%----------------------------------------------------------------------------
-spec start() -> {'error', {'already_started', 'undefined'}}.
start() ->
{error, {already_started,undefined}}.
-spec stop() -> 'ok'.
stop() ->
ok.
-spec load_driver(Path :: path(), Driver :: driver()) ->
'ok' | {'error', any()}.
load_driver(Path, Driver) ->
do_load_driver(Path, Driver, [{driver_options,[kill_ports]}]).
-spec load(Path :: path(), Driver :: driver()) ->
'ok' | {'error', any()}.
load(Path, Driver) ->
do_load_driver(Path, Driver, []).
do_load_driver(Path, Driver, DriverFlags) ->
case erl_ddll:try_load(Path, Driver,[{monitor,pending_driver}]++DriverFlags) of
{error, inconsistent} ->
{error,bad_driver_name}; % BC
{error, What} ->
{error,What};
{ok, already_loaded} ->
ok;
{ok,loaded} ->
ok;
{ok, pending_driver, Ref} ->
receive
{'DOWN', Ref, driver, _, load_cancelled} ->
{error, load_cancelled};
{'UP', Ref, driver, _, permanent} ->
{error, permanent};
{'DOWN', Ref, driver, _, {load_failure, Failure}} ->
{error, Failure};
{'UP', Ref, driver, _, loaded} ->
ok
end
end.
do_unload_driver(Driver,Flags) ->
case erl_ddll:try_unload(Driver,Flags) of
{error,What} ->
{error,What};
{ok, pending_process} ->
ok;
{ok, unloaded} ->
ok;
{ok, pending_driver} ->
ok;
{ok, pending_driver, Ref} ->
receive
{'UP', Ref, driver, _, permanent} ->
{error, permanent};
{'UP', Ref, driver, _, unload_cancelled} ->
ok;
{'DOWN', Ref, driver, _, unloaded} ->
ok
end
end.
-spec unload_driver(Driver :: driver()) -> 'ok' | {'error', any()}.
unload_driver(Driver) ->
do_unload_driver(Driver,[{monitor,pending_driver},kill_ports]).
-spec unload(Driver :: driver()) -> 'ok' | {'error', any()}.
unload(Driver) ->
do_unload_driver(Driver,[]).
-spec reload(Path :: path(), Driver :: driver()) ->
'ok' | {'error', any()}.
reload(Path,Driver) ->
do_load_driver(Path, Driver, [{reload,pending_driver}]).
-spec reload_driver(Path :: path(), Driver :: driver()) ->
'ok' | {'error', any()}.
reload_driver(Path,Driver) ->
do_load_driver(Path, Driver, [{reload,pending_driver},
{driver_options,[kill_ports]}]).
-spec format_error(Code :: atom()) -> string().
format_error(Code) ->
case Code of
%% This is the only error code returned only from erlang code...
%% 'permanent' has a translation in the emulator, even though the erlang code uses it to...
load_cancelled ->
"Loading was cancelled from other process";
_ ->
erl_ddll:format_error_int(Code)
end.
-spec info(Driver :: driver()) -> [{atom(), any()}, ...].
info(Driver) ->
[{processes, erl_ddll:info(Driver,processes)},
{driver_options, erl_ddll:info(Driver,driver_options)},
{port_count, erl_ddll:info(Driver,port_count)},
{linked_in_driver, erl_ddll:info(Driver,linked_in_driver)},
{permanent, erl_ddll:info(Driver,permanent)},
{awaiting_load, erl_ddll:info(Driver,awaiting_load)},
{awaiting_unload, erl_ddll:info(Driver,awaiting_unload)}].
-spec info() -> [{string(), [{atom(), any()}]}].
info() ->
{ok,DriverList} = erl_ddll:loaded_drivers(),
[{X,Y} || X <- DriverList,
Y <- [catch info(X)],
is_list(Y), not lists:member({linked_in_driver,true},Y)].