summaryrefslogtreecommitdiff
path: root/deps/bt/src/bt.erl
diff options
context:
space:
mode:
Diffstat (limited to 'deps/bt/src/bt.erl')
-rw-r--r--deps/bt/src/bt.erl355
1 files changed, 355 insertions, 0 deletions
diff --git a/deps/bt/src/bt.erl b/deps/bt/src/bt.erl
new file mode 100644
index 0000000..06beb35
--- /dev/null
+++ b/deps/bt/src/bt.erl
@@ -0,0 +1,355 @@
+%%%---- 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 : bt.erl
+%%% Author : Tony Rogvall <tony@iMac.local>
+%%% Description : Bluetooth utilities
+%%% Created : 31 Jan 2006 by Tony Rogvall <tony@iMac.local>
+
+-module(bt).
+
+-export([start/0, stop/0]).
+-export([i/0, i/1]).
+-export([li/0]).
+-export([s/1, s/2]).
+-export([debug/0, debug/1]).
+-export([scan/1, scan/3]).
+-export([getaddr/1, getaddr_by_name/1]).
+-export([format_address/1]).
+-export([service_info/1, service_info/2]).
+-export([rfcomm_channel/2]).
+-export([decode_service/1]).
+
+-import(lists, [foreach/2, map/2]).
+
+-include("../include/bt.hrl").
+-include("../include/uuid.hrl").
+-include("../include/sdp.hrl").
+
+start() ->
+ application:start(bt).
+
+stop() -> %% restart=
+ bt_drv:stop().
+
+
+debug() ->
+ bt_drv:debug(debug).
+
+debug(true) ->
+ bt_drv:debug(debug);
+debug(false) ->
+ bt_drv:debug(none);
+debug(Level) ->
+ bt_drv:debug(Level).
+
+%%
+%% Dump device information
+%%
+i() ->
+ {ok,Devices} = bt_drv:devices(),
+ foreach(fun(A) -> i(A) end, Devices).
+
+i(paired) ->
+ {ok, Devices} = bt_drv:paired_devices(),
+ foreach(fun(A) -> i(A) end, Devices);
+i(favorite) ->
+ {ok, Devices} = bt_drv:favorite_devices(),
+ foreach(fun(A) -> i(A) end, Devices);
+i(BtAddr) ->
+ {ok,A} = getaddr(BtAddr),
+ case bt_drv:device_info(A, [inquiry,update]) of
+ {ok, [{inquiry,?never},{update,_}]} ->
+ io:format("Address: ~s\n", [format_address(A)]);
+ {ok, [{inquiry,_InQuiry},{update,Update}]}->
+ io:format("Address: ~s\n", [format_address(A)]),
+ {ok,DevInfo} = bt_drv:device_info(A,[name,
+ is_paired,
+ is_favorite,
+ is_connected,
+ class]),
+ foreach(fun({class,Value}) ->
+ {Service,Major,Minor} = bt_drv:decode_class(Value),
+ io:format(" major: ~p\n", [Major]),
+ io:format(" minor: ~p\n", [Minor]),
+ io:format(" service: ~p\n", [Service]);
+ ({name,Name}) ->
+ io:format(" name: ~s\n", [Name]);
+ ({What,Value}) ->
+ io:format(" ~p: ~p\n", [What,Value])
+ end, DevInfo),
+ if Update == ?never ->
+ ok;
+ true ->
+ {ok,SdpInfo} = bt_drv:service_info(A),
+ io:format(" Profiles:"),
+ foreach(
+ fun(Service) ->
+ As = map(fun(A1) ->
+ bt_sdp:decode(A1)
+ end, Service),
+ case lists:keysearch(256, 1, As) of
+ false -> ok;
+ {value,{_,{text,Name}}} ->
+ io:format(" ~s,", [Name])
+ end
+ end, SdpInfo),
+ io:format("\n")
+ end;
+ Error -> Error
+ end.
+
+%%
+%% Dump information about the local bluetooth adapter
+%%
+li() ->
+ case bt_drv:local_info([name,class,address,discoverable,power_state]) of
+ {ok,Info} ->
+ io:format("Local Adapter:\n",[]),
+ foreach(fun({class,Value}) ->
+ {Service,Major,Minor} = bt_drv:decode_class(Value),
+ io:format(" major: ~p\n", [Major]),
+ io:format(" minor: ~p\n", [Minor]),
+ io:format(" service: ~p\n", [Service]);
+ ({name,Name}) ->
+ io:format(" name: ~s\n", [Name]);
+ ({address,Addr}) ->
+ io:format(" addr: ~s\n", [format_address(Addr)]);
+ ({What,Value}) ->
+ io:format(" ~p: ~p\n", [What,Value])
+ end, Info);
+ Error ->
+ Error
+ end.
+
+%%
+%% Dump service information
+%% s(Addr [UUID])
+%%
+%% Addr is either the Bluetooth address (as string or tuple) or
+%% the name of the device (a bit slow)
+%% UUID is UUID16 | UUID32 | UUID128 | Symbolic-Name
+%%
+s(Addr) ->
+ case bt_drv:service_info(Addr,<<>>) of
+ {ok, Info} when is_list(Info) ->
+ foreach(
+ fun(Service) when is_list(Service) ->
+ s_serv(Service),
+ io:format("\n")
+ end, Info);
+ Error ->
+ Error
+ end.
+
+s(Addr, UUID) when is_binary(UUID), size(UUID) > 0 ->
+ case bt_drv:service_info(Addr,UUID) of
+ {ok, Service} ->
+ s_serv(Service);
+ Error ->
+ Error
+ end;
+s(Addr, Name) when is_list(Name) ->
+ UUID = bt_sdp:string_to_uuid(Name),
+ case bt_drv:service_info(Addr,UUID) of
+ {ok, Service} ->
+ s_serv(Service);
+ Error ->
+ Error
+ end.
+
+s_serv(Attributes=[A1|_]) when is_binary(A1) ->
+ Attrs0 = map(fun(A) -> bt_sdp:decode(A) end, Attributes),
+ LangList = [{_,_,Base}|_] = get_language_base(Attrs0, 256),
+ UUID = case lists:keysearch(?ATTR_ServiceClassIDList, 1, Attrs0) of
+ false -> <<>>;
+ {value,{_,{sequence,[{uuid,UUID0}|_]}}} -> UUID0
+ end,
+ %% Fixme: present ServiceName,ServiceDescription,ProviderName with language base
+ %% and delete them before printing further data.
+ ServiceNames = s_attr(?ATTR_ServiceName, LangList, Attrs0, []),
+ ServiceDescs = s_attr(?ATTR_ServiceDescription, LangList, Attrs0, []),
+ ProviderNames = s_attr(?ATTR_ProviderName, LangList, Attrs0, []),
+
+ case ServiceNames of
+ [{_,{text,Name}}|_] -> io:format("Service Name: ~s\n", [Name]);
+ [] -> io:format("Service Name: None\n", [])
+ end,
+
+ case ServiceDescs of
+ [{_,{text,Desc}}|_] -> io:format(" Service Description: ~s\n", [Desc]);
+ [] -> ok
+ end,
+
+ case ProviderNames of
+ [{_,{text,Prov}}|_] -> io:format(" Provider Name: ~s\n", [Prov]);
+ [] -> ok
+ end,
+ Attrs1 = Attrs0 -- (ServiceNames++ServiceDescs++ProviderNames),
+ Attrs = lists:keydelete(?ATTR_LanguageBaseAttributeIDList, 1, Attrs1),
+ foreach(
+ fun ({ID,Value}) ->
+ AttrName = bt_sdp:attribute_to_string(ID,Base,UUID),
+ io:format(" ~s: ~s\n", [AttrName, bt_sdp:value_to_string(Value)])
+ end, Attrs).
+
+s_attr(Attr, [{_Lang,_Env,Base}|Ls], As, Acc) ->
+ case lists:keysearch(Attr+Base, 1, As) of
+ false ->
+ s_attr(Attr, Ls, As, Acc);
+ {value,Value} ->
+ s_attr(Attr, Ls, As, [Value|Acc])
+ end;
+s_attr(_Attr, [], _As, Acc) ->
+ lists:reverse(Acc).
+
+
+get_language_base(Attrs,Default) ->
+ case get_languages(Attrs) of
+ [] -> [{25966,106,Default}];
+ Ls -> Ls
+ end.
+
+get_languages(Attrs) ->
+ case lists:keysearch(?ATTR_LanguageBaseAttributeIDList, 1, Attrs) of
+ {value,{_,{sequence,Seq}}} -> get_lang3(Seq);
+ _ -> []
+ end.
+
+get_lang3([{uint16,Lang},{uint16,Encoding},{uint16,Base}|Ls]) ->
+ [{Lang,Encoding,Base} | get_lang3(Ls)];
+get_lang3([]) ->
+ [].
+
+
+%%
+%% Decode all services on a device
+%%
+
+service_info(Addr) ->
+ case bt_drv:service_info(Addr,<<>>) of
+ {ok, ServiceList} when is_list(hd(ServiceList)) ->
+ map(fun(Attributes) ->
+ decode_service(Attributes)
+ end, ServiceList);
+ Error ->
+ Error
+ end.
+%%
+%%
+%%
+service_info(Addr, Service) when is_list(Service) ->
+ service_info(Addr, bt_sdp:string_to_uuid(Service));
+service_info(Addr, UUID) when is_binary(UUID), size(UUID) > 0 ->
+ case bt_drv:service_info(Addr,UUID) of
+ {ok, Service} when is_binary(hd(Service)) ->
+ decode_service(Service);
+ Error ->
+ Error
+ end.
+%%
+%% Extract rfcomm channel for the given service
+%%
+rfcomm_channel(Addr, Service) when is_list(Service) ->
+ rfcomm_channel(Addr, bt_sdp:string_to_uuid(Service));
+rfcomm_channel(Addr, UUID) when is_binary(UUID), size(UUID) > 0 ->
+ case bt_drv:service_info(Addr,UUID) of
+ {ok, Service} when is_binary(hd(Service)) ->
+ As = decode_service(Service),
+ case lists:keysearch(?ATTR_ProtocolDescriptorList, 1, As) of
+ {value,{_, {sequence,[{sequence,["L2CAP"]},
+ {sequence,["RFCOMM",Channel]}|_]}}} ->
+ {ok,Channel};
+ _ ->
+ {error, no_channel}
+ end;
+ Error -> Error
+ end.
+
+%%
+%% Decode a binary encoded SDP list:
+%% [ binary(<attribute><value>) ] in SDP format
+%%
+
+decode_service(Attributes) when is_binary(hd(Attributes)) ->
+ Attrs = map(fun(A) -> bt_sdp:decode(A) end, Attributes),
+ map(fun({ID,Value}) ->
+ {ID, bt_sdp:decode_sdp_value(Value)}
+ end, Attrs).
+
+getaddr(Name) ->
+ bt_util:getaddr(Name).
+
+getaddr_by_name(Name) ->
+ bt_util:getaddr_by_name(Name).
+
+%%
+%% Inquiry scan:
+%% Note that the Fun can not make meaningfull remote calls
+%% while inquiry is running.
+%%
+%%
+scan(Timeout) ->
+ scan(Timeout, fun(Addr,Acc) -> {continue,[Addr|Acc]} end, []).
+
+scan(Timeout, Fun, Acc) ->
+ case bt_drv:inquiry_start(Timeout) of
+ {ok,Ref} ->
+ receive
+ {bt,Ref,started} ->
+ scan_loop(Ref, Fun, Acc)
+ end;
+ Error ->
+ Error
+ end.
+
+scan_loop(Ref, Fun, Acc) ->
+ receive
+ {bt,Ref,{device,Addr}} ->
+ case Fun(Addr,Acc) of
+ {continue,Acc1} ->
+ scan_loop(Ref, Fun, Acc1);
+ {stop,Acc1} ->
+ bt_drv:inquiry_stop(Ref),
+ {ok,Acc1}
+ end;
+ {bt,Ref, stopped} ->
+ bt_drv:inquiry_stop(Ref),
+ {ok,Acc}
+ end.
+
+%%
+%% Format bluetooth address into a hex string
+%%
+format_address(A) when ?is_bt_address(A) ->
+ case os:type() of
+ {unix,darwin} ->
+ format_address_(A, $-);
+ _ ->
+ format_address_(A, $:)
+ end;
+format_address(<<F,E,D,C,B,A>>) -> %% binary is reverse format
+ format_address({A,B,C,D,E,F}).
+
+format_address_({A,B,C,D,E,F}, S) ->
+ [hexh(A),hexl(A),S,hexh(B),hexl(B),S,hexh(C),hexl(C),S,
+ hexh(D),hexl(D),S,hexh(E),hexl(E),S,hexh(F),hexl(F)].
+
+hexl(A) -> hex1(A band 16#f).
+hexh(A) -> hex1((A bsr 4) band 16#f).
+
+hex1(A) when A < 10 -> A+$0;
+hex1(A) -> (A-10)+$a.