/*	       lac_mux.c   2.02.000 20JUN99 12:18  */

#include "lac_options.h"
#include "lac_types.h"
#include "lac_defaults.h"
#include "lac_machines.h"
/******************************************************************************
 * LAC : LINK AGGREGATION CONTROL PROTOCOL : MUX CONTROL & SELECTION LOGIC
 ******************************************************************************
 */
/*---------------------------------------------------------------------------*/
static Boolean partners_aport(Lac_port *aport, Lac_port *port)
{
   Lac_port *p = aport;
   do
   {
      if(  (p->actor.system_priority   == port->partner.system_priority     )
        && (p->actor.system_id         == port->partner.system_id           )
        && (p->actor.key               == port->partner.key                 )
        && (p->actor.port_no           == port->partner.port_no             )
        &&((p->actor.port_priority     <  port->actor.port_priority )
          ||( (p->actor.port_priority  == port->actor.port_priority )
            &&(p->actor.port_no        <  port->actor.port_no       )
        )   ) )
        return(True);
   }
   while ((p = p->alink) != aport);
   return(False);
}
/*---------------------------------------------------------------------------*/
static Lac_port *find_aport(Lac_port *port)
{
   Lac_port *ap0    = &port->system->ports;
   Lac_port *ap     = &port->system->ports;
   Lac_port *best   =  port;

   while ((ap = ap->next) != ap0)
   {
      if(  (ap->actor.system_priority     == port->actor.system_priority    )
        && (ap->actor.system_id           == port->actor.system_id          )
        && (ap->actor.key                 == port->actor.key                )
        && (ap->partner.system_priority   == port->partner.system_priority  )
        && (ap->partner.system_id         == port->partner.system_id        )
        && (ap->partner.key               == port->partner.key              )
        && (ap->actor.state.aggregation   &&   ap->partner.state.aggregation)
        && (port->actor.state.aggregation && port->partner.state.aggregation)
        &&((ap->actor.port_priority       <  best->actor.port_priority )
          ||( (ap->actor.port_priority    == best->actor.port_priority )
            &&(ap->actor.port_no          <  best->actor.port_no       )
          ) )
        && (!partners_aport(ap, port))
        )
        best = ap;
   }
   return(best);
}
/*---------------------------------------------------------------------------*/
static void select(Lac_port *port)
{/*
  */
   Lac_port *p;
   Lac_port  *ap = find_aport(port);
   
   if (port != port->aport) /* still linked to previous aggregate port */
      {
         p = port->aport;
         while (p->alink != port) p = p->alink;
         p->alink = port->alink; port->alink = port; port->aport = port;
      }

   if ((port->alink == port) || (port == ap)) /* no old attachments */
   {
      port->aport = ap; port->selected = True;
      if (port   != ap) 
      {
         port->alink = port->aport->alink; port->aport->alink = port;
      }

      if (port->actor.state.aggregation && port->partner.state.aggregation)
         port->attach_when = Aggregate_wait_ticks;
      else
         port->attach_when = Now;
}  }
/*---------------------------------------------------------------------------*/
static void attach(Lac_port *port)
{/*
  */
   Lac_port *p;
   /** check for detaching or waiting ports ... **/
   p = port->aport;
   while (   (p->selected) && (p->attach_when == Now)
         && ((p = p->alink) != port->aport)
         ){}
   if (p == port->aport) /* if none, attach all detached ports in alink */
   {
      do
      {
         if (!p->attach && !port->standby)
         {
            p->attach = True;
            hw_control(p, Lac_attach);
         }
      } while ((p = p->alink) != port->aport);
}  }
/*---------------------------------------------------------------------------*/
static void update_mux(Lac_port *port)
{/*
  */
   Lac_state *as = &port->actor.state, *ps = &port->partner.state;
   unsigned   actor_sync   = port->selected && !port->standby
                          && port->attach   &&  port->attached;
   unsigned   partner_sync = port->matched  &&  ps->synchronization;

   if (as->synchronization != actor_sync)
   {
         as->synchronization = actor_sync;
         tx_machine(port, Lac_ntt);
   }

   ps->synchronization = partner_sync;
   
   if (        ps->synchronization &&   ps->collecting
      &&       as->synchronization &&   as->collecting && !as->distributing)
   {
      as->distributing = True; /* tx_machine(port, Lac_ntt);*/
      hw_control(port, Lac_enable_distributor);
   }
   else if (   ps->synchronization
           &&  as->synchronization &&  !as->collecting)
   {
      hw_control(port, Lac_enable_collector);
   }
   else if (( !ps->synchronization  || !ps->collecting
           || !as->synchronization) &&                     as->distributing)
   {
      hw_control(port, Lac_disable_distributor);
   }
   else if (( !ps->synchronization
           || !as->synchronization) &&  as->collecting)
   {
      as->collecting = False;     tx_machine(port, Lac_ntt);
      hw_control(port, Lac_disable_collector);
   }
   else if (( !port->selected ||  port->standby)
           &&  port->attach   &&  port->attached)
   {
      port->attach = False;
      hw_control(port, Lac_detach);
   }
   else if (  !port->selected
           && !port->attach   && !port->attached)
   {
      select(port);
   }
   
   if (        port->selected && !port->standby && (port->attach_when == Now)
           && !port->attach   && !port->attached)
   {
      attach(port);
}  }
/*---------------------------------------------------------------------------*/
static void change_lag(Lac_port *port)
{/*
  */
   Lac_port  *p, *p0;

   p = p0 = &port->system->ports; /* reselect dependents, old and new */
   while ((p = p->next) != p0)
   {
      if (p->selected && (p->aport != find_aport(p)))
      {
          p->selected = False;
          update_mux(p);
}  }  }
/*---------------------------------------------------------------------------*/
extern void mux_control(Lac_port *port, Lac_event event)
{/*
  */
   switch (event)
   {
   case Lac_init:
      port->selected    = port->standby  = False;
      port->aport = port; port->alink = port;
      hw_control(port, Lac_init);
      port->actor.state.distributing  = port->actor.state.collecting = False;
      port->attach      = port->attached = False;
      port->attach_when = Stopped;
      return;

   case Lac_tick: if (port->attach_when != Now) port->attach_when--;
      break;

   case Lac_new_info: if (!port->selected) change_lag(port);
      break;

   case Lac_distributor_on:
      break;

   case Lac_distributor_off: port->actor.state.distributing = False;
                          /* tx_machine(port, Lac_ntt);*/
      break;

   case Lac_collector_on:    port->actor.state.collecting = True;
                             tx_machine(port, Lac_ntt);
      break;

   case Lac_collector_off:
      break;

   case Lac_detached:        port->attach = port->attached = False;
      break;

   case Lac_attached:        port->attached = True;
      break;

   default:
      break;
   }
   update_mux(port);
}
/*---------------------------------------------------------------------------*/
