-module(uds). -export([listen/1, connect/1, accept/1, send/2, recv/1, close/1, get_port/1, get_status_counters/1, set_mode/2, controlling_process/2, tick/1, get_creation/1]). -define(decode(A,B,C,D), (((A) bsl 24) bor ((B) bsl 16) bor ((C) bsl 8) bor (D))). -define(encode(N), [(((N) bsr 24) band 16#FF), (((N) bsr 16) band 16#FF), (((N) bsr 8) band 16#FF), ((N) band 16#FF)]). -define(check_server(), case whereis(uds_server) of undefined -> exit(uds_server_not_started); _ -> ok end). listen(Name) -> ?check_server(), command(port(),$L,Name). connect(Name) -> ?check_server(), command(port(),$C,Name). accept(Port) -> ?check_server(), case control(Port,$N) of {ok, N} -> command(port(),$A,N); Else -> Else end. send(Port,Data) -> ?check_server(), command(Port, $S, Data). recv(Port) -> ?check_server(), command(Port, $R, []). close(Port) -> ?check_server(), (catch unlink(Port)), %% Avoids problem with trap exits. case (catch erlang:port_close(Port)) of {'EXIT', Reason} -> {error, closed}; _ -> ok end. get_port(Port) -> ?check_server(), {ok,Port}. get_status_counters(Port) -> ?check_server(), case control(Port, $S) of {ok, {C0, C1, C2}} -> {ok, C0, C1, C2}; Other -> Other end. get_creation(Port) -> ?check_server(), case control(Port, $R) of {ok, [A]} -> A; Else -> Else end. set_mode(Port, command) -> ?check_server(), control(Port,$C); set_mode(Port,intermediate) -> ?check_server(), control(Port,$I); set_mode(Port,data) -> ?check_server(), control(Port,$D). tick(Port) -> ?check_server(), control(Port,$T). controlling_process(Port, Pid) -> ?check_server(), case (catch erlang:port_connect(Port, Pid)) of true -> (catch unlink(Port)), ok; {'EXIT', {badarg, _}} -> {error, closed}; Else -> exit({unexpected_driver_response, Else}) end. control(Port, Command) -> case (catch erlang:port_control(Port, Command, [])) of [0] -> ok; [0,A] -> {ok, [A]}; [0,A,B,C,D] -> {ok, [A,B,C,D]}; [0,A1,B1,C1,D1,A2,B2,C2,D2,A3,B3,C3,D3] -> {ok, {?decode(A1,B1,C1,D1),?decode(A2,B2,C2,D2), ?decode(A3,B3,C3,D3)}}; [1|Error] -> exit({error, list_to_atom(Error)}); {'EXIT', {badarg, _}} -> {error, closed}; Else -> exit({unexpected_driver_response, Else}) end. command(Port, Command, Parameters) -> SavedTrapExit = process_flag(trap_exit,true), case (catch erlang:port_command(Port,[Command | Parameters])) of true -> receive {Port, {data, [Command, $o, $k]}} -> process_flag(trap_exit,SavedTrapExit), {ok, Port}; {Port, {data, [Command |T]}} -> process_flag(trap_exit,SavedTrapExit), {ok, T}; {Port, Else} -> process_flag(trap_exit,SavedTrapExit), exit({unexpected_driver_response, Else}); {'EXIT', Port, normal} -> process_flag(trap_exit,SavedTrapExit), {error, closed}; {'EXIT', Port, Error} -> process_flag(trap_exit,SavedTrapExit), exit(Error) end; {'EXIT', {badarg, _}} -> process_flag(trap_exit,SavedTrapExit), {error, closed}; Unexpected -> process_flag(trap_exit,SavedTrapExit), exit({unexpected_driver_response, Unexpected}) end. port() -> SavedTrapExit = process_flag(trap_exit,true), case open_port({spawn, "uds_drv"},[]) of P when port(P) -> process_flag(trap_exit,SavedTrapExit), P; {'EXIT',Error} -> process_flag(trap_exit,SavedTrapExit), exit(Error); Else -> process_flag(trap_exit,SavedTrapExit), exit({unexpected_driver_response, Else}) end.