anchor.diff:  %%% @copyright (C) 1999-2013, Erlang Solutions Ltd
   %%% @author Marc Sugiyama <marc.sugiyama@erlang-solutions.com>
   %%% @doc 
  -%%% API for ofs_handler
  +%%% ofs_handler provides a higher level, somewhat abstracted interface
  +%%% to an OpenFlow switch.  It provides functions to send requests to
  +%%% the switch and subscribe to messages from the switch.  ofs_handler_driver
  +%%% should be used as the callback module with of_driver.  ofs_handler also
  +%%% has a callback interface.  The application environment variable
  +%%% `callback_module' is the default callback module.  `callback_opt' is
  +%%% a term() passed to the Module:init/7 and Module:connect/8 callback
  +%%% functions.  The Opt passed from of_driver overrides these values.
  +%%%
  +%%% ofs_handler always uses the main connection to send requests to the switch.
  +%%% It creates separate ofs_handler_message pids to handle messages
  +%%% received from the switch.  This allows for concurrent processing of
  +%%% asynchronous messages from the switch.
  +%%% 
  +%%% === of_driver Callbacks ===
  +%%% `ofs_handler_driver' is the of_driver callback module for ofs_handler.
  +%%%
  +%%% `ofs_handler_driver:init' - Accept the new main connection from the
  +%%% switch.  The Options passed from of_driver is a property list.  The
  +%%% keys 'callback_module' and 'callback_opt' override the values from
  +%%% ofs_handler's application environment.
  +%%%
  +%%% `ofs_handler_driver:handle_connect' - Accept a new auxiliary connection
  +%%% from the switch.  The Options passed from of_driver is a property list.
  +%%% The keys 'callback_module' and 'callback_opt' override the values from
  +%%% ofs_handler's application environment.
  +%%%
  +%%% `ofs_handler_driver:handle_message' - Accept message received from
  +%%% the switch.
  +%%%
  +%%% `ofs_handler_driver:handle_error' - Accept error received from the
  +%%% switch.
  +%%%
  +%%% `ofs_handler_driver:handle_disconnect' - Terminates handling of an
  +%%% auxiliary connection.
  +%%%
  +%%% `ofs_handler_driver:terminate' - Terminates handling of the main
  +%%% connection.  ofs_handler can no longer talk to the switch.
  +%%%
  +%%% === Callback Functions ===
  +%%% `Module:init(active, IpAddr, DatapathId, Features, Version,
  +%%% Connection, Opt) -> {ok, State} | {error, Reason}.' - Accept a new
  +%%% main connection.  `active' identifies it is the active ofs_handler
  +%%% that has received the new main connection.  `IpAddr' is the Ip
  +%%% address of the switch (`{A, B, C, D}' format), `DatapathId' is the
  +%%% datapath id of the switch, `Features' is the decoded `features_reply'
  +%%% message for the switch, `Version' is the protocol version, `Connection'
  +%%% is the of_driver connection, and `Opt' is the `callback_opt'.  The function
  +%%% returns `{ok, State}' where `State' is any Erlang term that is then
  +%%% passed to other callback functions.  The `State' is associated with
  +%%% the `Connection', so the callback functions may use `State' to identify
  +%%% the connection that triggered the callback.
  +%%%
  +%%% `Module:connect(active, IpAddr, DatapathId, Features, Version,
  +%%% Connection, AuxId, Opt) -> {ok, State} | {error, Reason}.' - Accept
  +%%% a new auxiliary connection.  The caller may assume that there is
  +%%% already a main connection from the same switch.  The parameters and
  +%%% return are the same as for `Module:init' with the addition of the
  +%%% `AuxId' parameter.  This is the auxiliary connection Id which was
  +%%% read from `Features'.
  +%%%
  +%%% `Module:disconnect(State) -> ok.' - Called when an auxiliary connection
  +%%% disconnects.  `State' is the Erlang term returned to ofs_handler
  +%%% from the `Module:connect' callback for the disconnecting auxiliary
  +%%% connection.  The callback module may want to store identifying information
  +%%% in `State'.
  +%%%
  +%%% `SubscriptionModule:handle_message(Msg, State) -> ok.' - Called when
  +%%% a message is received from the switch that matches a subscription.
  +%%% Note that the callback module for subscriptions may differ from
  +%%% module for the other callback functions.  Create subscriptions using
  +%%% `ofs_handler:subscribe/3'.
  +%%%
  +%%% `Module:handle_error(Reason, State) -> ok.' - Called when an error
  +%%% occurs on a connection.  'State' is the Erlang term returned to ofs_handler
  +%%% from `Module:init' or `Module:connect'.
  +%%%
  +%%% `Module:terminate(State) -> ok.' - Called when the main connection
  +%%% disconnects.  `State' is the Erlang term returned to ofs_handler
  +%%% from the `Module:init'.
  +%%% 
   %%% @end
   %%%-------------------------------------------------------------------
   -module(ofs_handler).
  
positive.code: % @doc
  % Start a ofs_handler process for the switch identified by DatapathId.
  % @end
  start_link(DatapathId) ->
      ofs_handler_logic_sup:start_child(DatapathId).
  
  % @doc
  % @end
  send(DatapathId, Msg) ->
      ofs_handler_logic:call_active(DatapathId, {send, Msg}).
  
  sync_send(DatapathId, Msg) ->
      ofs_handler_logic:call_active(DatapathId, {sync_send, Msg}).
  
  send_list(DatapathId, Msgs) ->
      ofs_handler_logic:call_active(DatapathId, {send_list, Msgs}).
  
  sync_send_list(DatapathId, Msgs) ->
      ofs_handler_logic:call_active(DatapathId, {sync_send_list, Msgs}).
  
  ping_switch(DatapathId) ->
      % send echo, wait for response
      ofs_handler_logic:call_active(DatapathId, ping_switch).
  
  subscribe(DatapathId, Module, Item) ->
      ofs_handler_logic:call_active(DatapathId, {async_subscribe, Module, Item}).
  
  unsubscribe(DatapathId, Module, Item) ->
      ofs_handler_logic:call_active(DatapathId, {async_unsubscribe, Module, Item}).
  
  get_subscriptions(DatapathId, Module) ->
      ofs_handler_logic:call_active(DatapathId, {get_async_subscribe, Module}).
  
  terminate(DatapathId) ->
      ofs_handler_logic:call_active(DatapathId, terminate).
  
  %%% ===========================================================================
  %%% Internal functions
  %%% ===========================================================================
  
negative.code:     terminate/1
  ]).
  
  -include_lib("of_protocol/include/of_protocol.hrl").
  
  -type datapath_mac() :: binary().
  -type datapath_lsid() :: integer().
  -type datapath_id() :: {datapath_lsid(), datapath_mac()}.
  -type error_reason() :: atom().
  -type subscription_type() :: atom().
  -type subscription_filter() :: function().
  -type subscription_item() :: subscription_type() | {subscription_type(), subscription_filter()}.
  
  %%% ===========================================================================
  %%% Public API
  %%% ===========================================================================
  