/*	       lac_rx.c   2.02.000 20JUN99 12:19  */

#include "lac_options.h"
#include "lac_types.h"
#include "lac_defaults.h"
#include "lac_machines.h"
/******************************************************************************
 * LAC : LINK AGGREGATION CONTROL PROTOCOL : RECEIVE, MATCH, & PERIODIC
 ******************************************************************************
 */
/*---------------------------------------------------------------------------*/
static Boolean same_port(Lac_info *a, Lac_info *b)
{
   return (  (a->port_no               == b->port_no)
          && (a->system_id             == b->system_id)
          );
}
/*---------------------------------------------------------------------------*/
static Boolean same_partner(Lac_info *a, Lac_info *b)
{/*
  */
   return (  (a->port_priority       == b->port_priority)
          && (a->port_no             == b->port_no)
          && (a->system_priority     == b->system_priority)
          && (a->system_id           == b->system_id)
          && (a->key                 == b->key)
          && (a->state.aggregation   == b->state.aggregation)
          );
}
/*---------------------------------------------------------------------------*/
static void copy_info(Lac_info *from, Lac_info *to)
{/*
  */
   to->port_priority         = from->port_priority;
   to->port_no               = from->port_no;
   to->system_priority       = from->system_priority;
   to->system_id             = from->system_id;
   to->key                   = from->key;
   to->state.lacp_activity   = from->state.lacp_activity;
   to->state.lacp_timeout    = from->state.lacp_timeout;
   to->state.aggregation     = from->state.aggregation;
   to->state.synchronization = from->state.synchronization;
   to->state.collecting      = from->state.collecting;
   to->state.distributing    = from->state.distributing;
   to->state.defaulted       = from->state.defaulted;
   to->state.expired         = from->state.expired;
}
/*---------------------------------------------------------------------------*/
static void actor_default(Lac_port *port)
{
   copy_info(&port->actor_admin, &port->actor);
}
/*---------------------------------------------------------------------------*/
static void record_pdu(Lac_port *port, Lac_pdu *pdu)
{
   copy_info(&pdu->actor, &port->partner);
   port->actor.state.defaulted = False;
}
/*---------------------------------------------------------------------------*/
static void record_default(Lac_port *port)
{
   copy_info(&port->partner_admin, &port->partner);
   port->actor.state.defaulted = True;
}
/*---------------------------------------------------------------------------*/
static void update_selected(Lac_port *port, Lac_pdu *pdu)
{
   if (!same_partner(&pdu->actor, &port->partner))
   {
      port->selected                    = False;
      port->standby                     = False;
      port->actor.state.synchronization = False;
}  }
/*---------------------------------------------------------------------------*/
static void update_default_selected(Lac_port *port)
{
   if (!same_partner(&port->partner_admin, &port->partner))
   {
      port->selected                    = False;
      port->standby                     = False;
      port->actor.state.synchronization = False;
}  }
/*---------------------------------------------------------------------------*/
static void choose_matched(Lac_port *port, Lac_pdu *pdu)
{
   if (  (  (   same_partner(&pdu->partner, &port->actor)
            && (pdu->partner.state.aggregation ==
                port->actor.state.aggregation)
            )
         || (  !pdu->actor.state.aggregation
         )  )
      && (  (   pdu->actor.state.lacp_activity
            )
         || (   port->actor.state.lacp_activity
            &&  pdu->partner.state.lacp_activity
      )  )  )
      port->matched = True;
   else
      port->matched = False;
}
/*---------------------------------------------------------------------------*/
static void update_ntt(Lac_port *port, Lac_pdu *pdu)
{
   if ( !(same_partner(&pdu->partner, &port->actor))
      || (pdu->partner.state.lacp_activity   !=
          port->actor.state.lacp_activity)
      || (pdu->partner.state.aggregation     !=
          port->actor.state.aggregation)
      || (pdu->partner.state.synchronization !=
          port->actor.state.synchronization)
      || (pdu->partner.state.collecting      !=
          port->actor.state.collecting)
      )
      tx_machine(port, Lac_ntt);
}
/*---------------------------------------------------------------------------*/
static void start_current_while_timer(Lac_port *port, Boolean timeout)
{
   if (timeout == Short_timeout)
      port->current_while = Short_timeout_ticks;
   else
      port->current_while = Long_timeout_ticks;
}
/*---------------------------------------------------------------------------*/
static void rxm_initialize(Lac_port *port)
{
   port->selected = False;
   port->standby  = False;
   actor_default(port);
   record_default(port);
   port->actor.state.expired = False;
   port->rxm      = Rxm_initialize;
}
/*---------------------------------------------------------------------------*/
static void rxm_port_disabled(Lac_port *port)
{
   port->matched  = False;
   port->rxm      = Rxm_port_disabled;
}
/*---------------------------------------------------------------------------*/
static void rxm_lacp_disabled(Lac_port *port)
{
   port->selected                  = False;
   port->standby                   = False;
   record_default(port);
   port->partner.state.aggregation = False;
   port->matched                   = True;
   port->rxm                       = Rxm_lacp_disabled;
}
/*---------------------------------------------------------------------------*/
static void rxm_expired(Lac_port *port)
{
   port->matched                    = False;
   port->partner.state.lacp_timeout = Short_timeout;
   start_current_while_timer(port, Short_timeout);
   port->actor.state.expired        = True;
   port->rxm                        = Rxm_expired;
}
/*---------------------------------------------------------------------------*/
static void rxm_defaulted(Lac_port *port)
{
   update_default_selected(port);
   record_default(port);
   port->matched             = True;
   port->actor.state.expired = False;
   port->rxm                 = Rxm_defaulted;
}
/*---------------------------------------------------------------------------*/
static void rxm_current(Lac_port *port, Lac_pdu *pdu)
{
   update_selected(port, pdu);
   update_ntt(     port, pdu);
   record_pdu(     port, pdu);
   choose_matched( port, pdu);
   start_current_while_timer(port, port->actor.state.lacp_timeout);
   port->actor.state.expired = False;
   port->rxm                 = Rxm_current;
}
/*---------------------------------------------------------------------------*/
extern void rx_machine(Lac_port *port, Lac_event event, Lac_pdu *pdu)
{/*
  */
   switch (event)
   {
   case Lac_check_moved:
      if (   (port->rxm != Rxm_port_disabled)
          || !same_port(&port->partner, &pdu->actor)
         )
         break;
      /* otherwise continue to reinitialize */
   case Lac_init:
      rxm_initialize(port);
      /* continue - unconditional transition */
   case Lac_port_disabled:
      port->port_enabled      = False;
      rxm_port_disabled(port);
     break;

   case Lac_port_enabled:
      if (port->rxm != Rxm_port_disabled) break;

      if (port->lacp_enabled)
         rxm_expired(port);
      else
         rxm_lacp_disabled(port);
      break;

   case Lac_tick:
      if (sys_check_expiry(&port->current_while))
      {
         if (port->rxm == Rxm_current)
            rxm_expired(port);
         else if (port->rxm == Rxm_expired)
            rxm_defaulted(port);
      }
      break;

   case Lac_received:
      if ((port->rxm == Rxm_lacp_disabled)||(port->rxm == Rxm_port_disabled))
         break;
      rxm_current(port, pdu);
      break;
   default:
      break;
}  }
/*---------------------------------------------------------------------------*/
