anchor.diff:  % @doc
   % Start a ofs_handler process for the switch identified by DatapathId.
   % @end
  +-spec start_link(datapath_id()) -> {ok, pid()}.
   start_link(DatapathId) ->
       ofs_handler_logic_sup:start_child(DatapathId).
   
   % @doc
  -% @end
  +% Send a request to the switch with the DatapathId.  The reply, if
  +% any, is returned via the Module:handle_message/2 callback.  An error
  +% occurs if there was a problem sending the request to the switch.  It
  +% does not indicate an error return from the switch.
  +% @end
  +-spec send(datapath_id(), ofp_message()) -> ok | {error, error_reason()}.
   send(DatapathId, Msg) ->
       ofs_handler_logic:call_active(DatapathId, {send, Msg}).
   
  +% @doc
  +% Send a request to the switch with the DatapathId.  Implicitly
  +% adds a barrier request and waits for any replies.  The reply is
  +% returned to the caller as an ofp_message() that may be
  +% decoded using of_msg_lib:decode/1.  Returns `no_reply' if there
  +% was no reply.  An error occurs if there was
  +% a problem sending the request to the switch.  An error does not indicate
  +% an error response from the switch.
  +% @end
  +-spec sync_send(datapath_id(), ofp_message()) ->
  +                {ok, ofp_message() | noreply} | {error, error_reason()}.
   sync_send(DatapathId, Msg) ->
       ofs_handler_logic:call_active(DatapathId, {sync_send, Msg}).
   
  +% @doc
  +% Send a list of requests to the switch with the DatapathId.  Replies,
  +% if any, are returned via the Module:handler_message/2 callback.  Returns
  +% `ok' if there are no errors.  Returns `error' if one or more requests
  +% resulted in an error.  The accompanying list has one element for
  +% each request in the request list in the corresponding order.
  +% Errors occur when there is a problem sending the
  +% request to the switch.  An error does not indicate an error response
  +% from the switch.
  +% @end
  +-spec send_list(datapath_id(), [ofp_message()]) ->
  +                                    ok | {error, [ok | error_reason()]}.
   send_list(DatapathId, Msgs) ->
       ofs_handler_logic:call_active(DatapathId, {send_list, Msgs}).
   
  +% @doc
  +% Send a list of requests to the switch with the DatapathId.  Implicitly
  +% adds a barrier request after sending the list of requests and
  +% waits for any replies.  Replies are returned to the caller as ofp_message()
  +% records that may be decoded using of_msg_lib:decode/1.  The replies are
  +% returned in a list with one element for each request in the corresponding
  +% order.  Returns `no_reply' if the request had no reply.  An error
  +% occurs when there is a problem sending the request to the switch.  The
  +% accompanying list has one element for each request in the request list
  +% in the corresponding order.  An error does not indicate an error
  +% response from the switch.
  +% @end
  +-spec sync_send_list(datapath_id(), [ofp_message()]) ->
  +            {ok, [{ok, ofp_message() | no_reply}]} |
  +            {error, error_reason(),
  +                [{ok, ofp_message() | no_reply | {error, error_reason()}}]}.
   sync_send_list(DatapathId, Msgs) ->
       ofs_handler_logic:call_active(DatapathId, {sync_send_list, Msgs}).
   
  +% @doc
  +% Send an echo request to the switch with the DatapathId
  +% and wait for a response. Returns `pong' if the switch is alive
  +% and `pang' if not.
  +% @end
  +-spec ping_switch(datapath_id()) -> pong | pang.
   ping_switch(DatapathId) ->
       % send echo, wait for response
       ofs_handler_logic:call_active(DatapathId, ping_switch).
   
  +% @doc
  +% Add a callback subscription for asynchronous messages from the switch at
  +% DatapathId matching Item.  The callback function is in Module.  The
  +% item is a of_msg_lib message type (e.g., echo_reply) or a tuple with the
  +% message type and a filter function.
  +%
  +% If the Item is a message type atom, when the switch sends a message of that
  +% type to ofs_handler, ofs_handler calls Module:handle_message/2
  +% with the message.
  +%
  +%If the Item is a message type, filter function tuple, when the switch
  +% sends a message of that type to ofs_handler, ofs_handler calls the
  +% filter function with the message.  The message is a ofp_message record.
  +% If the filter function returns true, ofs_handler
  +% calls Module:handle_message/2 with the message.  Otherwise, ofs_handler
  +% discards the message.
  +%
  +% Messages with no subscriptions are discarded.
  +%
  +% There may be any number of subscriptions to the same message type.
  +% ofs_handler calls Module:handle_message/2 for every matching subscription.
  +% @end
  +-spec subscribe(datapath_id(), module(), subscription_item()) -> ok.
   subscribe(DatapathId, Module, Item) ->
       ofs_handler_logic:call_active(DatapathId, {async_subscribe, Module, Item}).
   
  +% @doc
  +% Remove a callback subscription for asynchronous messages from the switch
  +% at DatapathId for the matching Item.  After calling unsubscribe/3,
  +% Module will no longer recieve callbacks for Item.
  +% @end
  +-spec unsubscribe(datapath_id(), module(), subscription_item()) -> ok.
   unsubscribe(DatapathId, Module, Item) ->
       ofs_handler_logic:call_active(DatapathId, {async_unsubscribe, Module, Item}).
   
  +% @doc
  +% Get the current list of callback subscriptions for the switch at DatapathId
  +% for Module.  FilterFunction is `true' if the subscription did not have
  +% a filter function.
  +% @end
  +-spec get_subscriptions(datapath_id(), module()) ->
  +            [{MessageType :: subscription_type(),
  +              Module :: module(),
  +              FilterFunction :: subscription_filter() | true}].
   get_subscriptions(DatapathId, Module) ->
       ofs_handler_logic:call_active(DatapathId, {get_async_subscribe, Module}).
   
  +% @doc
  +% Terminate the ofs_handler for the switch at DatapathId.
  +% @end
  +-spec terminate(datapath_id()) -> ok.
   terminate(DatapathId) ->
       ofs_handler_logic:call_active(DatapathId, terminate).
  -
  -%%% ===========================================================================
  -%%% Internal functions
  -%%% ===========================================================================
  
positive.code: %%% @copyright (C) 1999-2013, Erlang Solutions Ltd
  %%% @author Marc Sugiyama <marc.sugiyama@erlang-solutions.com>
  %%% @doc 
  %%% API for ofs_handler
  %%% @end
  %%%-------------------------------------------------------------------
  -module(ofs_handler).
  
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
  %%% ===========================================================================
  