diff options
Diffstat (limited to 'deps/bt/src/obex_tcp.erl')
-rw-r--r-- | deps/bt/src/obex_tcp.erl | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/deps/bt/src/obex_tcp.erl b/deps/bt/src/obex_tcp.erl new file mode 100644 index 0000000..9067a3d --- /dev/null +++ b/deps/bt/src/obex_tcp.erl @@ -0,0 +1,224 @@ +%%%---- BEGIN COPYRIGHT ------------------------------------------------------- +%%% +%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se> +%%% +%%% This software is licensed as described in the file COPYRIGHT, which +%%% you should have received as part of this distribution. The terms +%%% are also available at http://www.rogvall.se/docs/copyright.txt. +%%% +%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell +%%% copies of the Software, and permit persons to whom the Software is +%%% furnished to do so, under the terms of the COPYRIGHT file. +%%% +%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +%%% KIND, either express or implied. +%%% +%%%---- END COPYRIGHT --------------------------------------------------------- +%%%------------------------------------------------------------------- +%%% File : obex_tcp.erl +%%% Author : Tony Rogvall <tony@PBook.local> +%%% Description : OBEX TCP API +%%% +%%% Created : 31 May 2006 by Tony Rogvall <tony@PBook.local> +%%%------------------------------------------------------------------- +-module(obex_tcp). + +-behaviour(gen_server). + +%% SERVER +-export([server_link/2, server/2]). +-export([stop/1]). +-export([server_accept/3]). +-export([register/3, unregister/2, lookup/2]). + +-export([open/3, close/1, listen/2, accept/1, send/2]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-ifdef(debug). +-define(dbg(Fmt,As), io:format("~s:~w:" Fmt "\n", [?FILE,?LINE | As])). +-else. +-define(dbg(Fmt,As), ok). +-endif. + + +-record(state, + { + channel, %% listen channel + ref, %% ListenRef + apid, %% Current accept process + targets=[], %% Targets associated + opts %% Options + }). + +%%==================================================================== +%% API +%%==================================================================== + +open(Address, Port, Opts) -> + gen_tcp:connect(Address, Port, Opts). + +listen(Port, Opts) -> + gen_tcp:listen(Port, Opts). + +accept(Listen) -> + gen_tcp:accept(Listen). + +close(Tcp) -> + gen_tcp:close(Tcp). + +send(Tcp, Data) -> + gen_tcp:send(Tcp, Data). + +register(Server, Target, Module) -> + gen_server:call(Server, {register, Target, Module}). + +unregister(Server, Target) -> + gen_server:call(Server, {register, Target}). + +lookup(Server, Target) -> + gen_server:call(Server, {lookup, Target}). + +%%-------------------------------------------------------------------- +%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +%% Description: Starts the server +%%-------------------------------------------------------------------- + +server_link(Port, Opts) -> + gen_server:start_link(?MODULE, [Port,Opts], []). + +server(Port, Opts) -> + gen_server:start(?MODULE, [Port,Opts], []). + +stop(Server) -> + gen_server:call(Server, stop). +%%==================================================================== +%% gen_server callbacks +%%==================================================================== + +%%-------------------------------------------------------------------- +%% Function: init(Args) -> {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%% Description: Initiates the server +%%-------------------------------------------------------------------- +init([Port,Opts]) -> + case listen(Port, [binary]) of + {ok,ListenRef} -> + process_flag(trap_exit, true), + APid = proc_lib:spawn_link(?MODULE, server_accept, [self(), ListenRef,Opts]), + {ok, #state { channel=Port, + ref = ListenRef, + apid = APid, + targets=opt_targets(Opts), + opts = Opts }}; + Error -> + {stop, Error} + end. + +%%-------------------------------------------------------------------- +%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | +%% {reply, Reply, State, Timeout} | +%% {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, Reply, State} | +%% {stop, Reason, State} +%% Description: Handling call messages +%%-------------------------------------------------------------------- +handle_call(stop, _From, State) -> + {stop, normal, ok, State}; +handle_call({register, Target, Module}, _From, State) -> + Targets = State#state.targets, + case lists:keysearch(Target, 1, Targets) of + false -> + State1 = State#state { targets = [{Target,Module}|Targets]}, + {reply, ok, State1}; + {value,_} -> + {reply, {error, already_registered}, State} + end; +handle_call({unregister, Target}, _From, State) -> + Targets = lists:keydelete(Target, 1, State#state.targets), + {reply, ok, State#state { targets = Targets }}; +handle_call({lookup, Target}, _From, State) -> + case lists:keysearch(Target, 1, State#state.targets) of + false -> {reply, {error, not_found}, State}; + {value,{_, Module}} -> {reply, {ok, Module}, State} + end; +handle_call(_Request, _From, State) -> + {reply, {error, bad_call}, State}. + +%%-------------------------------------------------------------------- +%% Function: handle_cast(Msg, State) -> {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} +%% Description: Handling cast messages +%%-------------------------------------------------------------------- +handle_cast({accept,Pid,{error,closed}}, State) when Pid == State#state.apid -> + {stop, closed, State}; %% Listen socket has closed +handle_cast({accept,Pid,Reason}, State) when Pid == State#state.apid -> + io:format("~s: accept ~p\n", [?MODULE, Reason]), + unlink(State#state.apid), + APid = proc_lib:spawn_link(?MODULE, server_accept, [self(), State#state.ref, State#state.opts]), + {noreply, State#state { apid = APid }}; +handle_cast({'EXIT',Pid,Reason}, State) when Pid == State#state.apid -> + io:format("~s: EXIT ~p\n", [?MODULE, Reason]), + APid = proc_lib:spawn_link(?MODULE, server_accept, [self(), State#state.ref, State#state.opts]), + {noreply, State#state { apid = APid }}; +handle_cast(_Msg, State) -> + {noreply, State}. + +%%-------------------------------------------------------------------- +%% Function: handle_info(Info, State) -> {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} +%% Description: Handling all non call/cast messages +%%-------------------------------------------------------------------- +handle_info(_Info, State) -> + {noreply, State}. + +%%-------------------------------------------------------------------- +%% Function: terminate(Reason, State) -> void() +%% Description: This function is called by a gen_server when it is about to +%% terminate. It should be the opposite of Module:init/1 and do any necessary +%% cleaning up. When it returns, the gen_server terminates with Reason. +%% The return value is ignored. +%%-------------------------------------------------------------------- +terminate(_Reason, _State) -> + ok. + +%%-------------------------------------------------------------------- +%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} +%% Description: Convert process state when code is changed +%%-------------------------------------------------------------------- +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- + +opt_targets(Opts) -> + opt_targets(Opts,[]). + +opt_targets([{target,{Target,Module}}|Opts], Ts) + when is_list(Target), is_atom(Module) -> + opt_targets(Opts, [{Target,Module}|Ts]); +opt_targets([_|Opts], Ts) -> + opt_targets(Opts, Ts); +opt_targets([], Ts) -> + Ts. + + +server_accept(Server, ListenRef, Opts) -> + case accept(ListenRef) of + {ok,ARef} -> + gen_server:cast(Server, {accept, self(), ok}), + {ok,{Address,_Port}} = inet:peername(ARef), + {ok,{_Address,Port}} = inet:sockname(ListenRef), + obex:server_init(Server, ARef, Address, Port, ?MODULE, Opts); + Error -> + gen_server:cast(Server, {accept, self(), Error}) + end. |