/*	lac.c   2.02.000 20JUN99 12:15  */

#include "sys.h"
#include "lac_options.h"
#include "lac_types.h"
#include "lac_defaults.h"
#include "lac_machines.h"
#include "lac.h"
/******************************************************************************
 * LAC : LINK AGGREGATION CONTROL PROTOCOL : COMPONENT INTERFACE
 ******************************************************************************
 */
/*---------------------------------------------------------------------------*/
extern void lac_rx(Lac_port *port, Lac_pdu *pdu)
{
   Lac_port *p;

   rx_machine( port, Lac_received, pdu);
   mux_control(port, Lac_new_info);
      
   p = &port->system->ports;
   while ((p = p->next) != &port->system->ports)
   {
      if (p != port)
         rx_machine(port, Lac_check_moved, pdu);
}  }
/*---------------------------------------------------------------------------*/
extern void lac_tick(Lac_port *port)
{
   rx_machine(      port, Lac_tick, NULL);
   mux_control(     port, Lac_new_info);
   mux_control(     port, Lac_tick);
   periodic_machine(port, Lac_tick);
   tx_machine(      port, Lac_tick);

   sys_start_timer(&port->tick_timer, Lac_ticks);
}
/******************************************************************************
 * LAC : LINK AGGREGATION CONTROL PROTOCOL : MANAGEMENT
 ******************************************************************************
 */
/*---------------------------------------------------------------------------*/
static void init_port(Lac_port *port, Boolean lacp_enabled)
{
   port->port_enabled = False;
   port->lacp_enabled = lacp_enabled;

   tx_machine(      port, Lac_init);
   periodic_machine(port, Lac_init);
   mux_control(     port, Lac_init);
   rx_machine(      port, Lac_init, NULL);

   if (port != &port->system->ports)
   {
      sys_cstop_timer(&port->tick_timer);
      sys_start_timer(&port->tick_timer, Lac_ticks);
}  }
/*---------------------------------------------------------------------------*/
static void enable_port(Lac_port *port)
{
   port->port_enabled = True;

   tx_machine(      port, Lac_port_enabled);
   periodic_machine(port, Lac_port_enabled);
   rx_machine(      port, Lac_port_enabled, NULL);
   mux_control(     port, Lac_new_info);
   mux_control(     port, Lac_port_enabled);
}
/*---------------------------------------------------------------------------*/
static void disable_port(Lac_port *port)
{
   port->port_enabled = True;

   tx_machine(      port, Lac_port_disabled);
   periodic_machine(port, Lac_port_disabled);
   rx_machine(      port, Lac_port_disabled, NULL);
   mux_control(     port, Lac_new_info);
   mux_control(     port, Lac_port_disabled);
}
/*---------------------------------------------------------------------------*/
static Boolean find_port(Lac_system *system, Port_no port_no, Lac_port **port)
{
   Lac_port *p;

   p = &system->ports;
   while ((p = p->next) != &system->ports)
   {
      if (p->port_no == port_no)
      {
         *port = p; return(True);
   }  }
   return(False);
}
/*---------------------------------------------------------------------------*/
extern void lac_init_system(Lac_system *system)
{
   Lac_port *p;

   p = &system->ports;
   while ((p = p->next) != &system->ports)
      init_port(p, Lacp_enabled);
}
/*---------------------------------------------------------------------------*/
extern void lac_init_port(Lac_system *system, Port_no port_no,
                          Boolean lacp_enabled)
{
   Lac_port *p;
   if (find_port(system, port_no, &p))
   {
      init_port(p, lacp_enabled);
      enable_port(p);
}  }
/*---------------------------------------------------------------------------*/
extern void lac_enable_port(Lac_system *system, Port_no port_no)
{
   Lac_port *p;
   if (find_port(system, port_no, &p)) enable_port(p);
}
/*---------------------------------------------------------------------------*/
extern void lac_disable_port(Lac_system *system, Port_no port_no)
{
   Lac_port *p;
   if (find_port(system, port_no, &p)) disable_port(p);
}
 /******************************************************************************
 * LAC : LINK AGGREGATION CONTROL PROTOCOL : PHYSICAL LINK RECEPTION & STATUS
 ******************************************************************************
 */
/*---------------------------------------------------------------------------*/
static void mac_rx(Node *mac, void *pdu)
{
   Lac_pdu  *lacpdu = (Lac_pdu  *)pdu;
   Lac_port *port   = (Lac_port *)mac->owner;

   if (   (lacpdu->ethertype        == Slow_protocols_ethertype)
       && (lacpdu->protocol_subtype == Lacp_subtype)
      )
   {
   lac_rx(port, lacpdu);
}  }

/*---------------------------------------------------------------------------*/
static void mac_status(Node *mac, Boolean yes, void *null)
{
   Lac_event mac_event = Lac_null;

   if (mac->tx_enabled && mac->rx_enabled)
      rx_machine((Lac_port *)mac->owner, Lac_port_enabled, NULL);
   else if (!yes && (mac->tx_enabled || mac->rx_enabled))
                 /* tx and rx both previously enabled */
      rx_machine((Lac_port *)mac->owner, mac_event, NULL);
}
/******************************************************************************
 * LAC : LINK AGGREGATION CONTROL PROTOCOL : CREATE SYSTEM
 ******************************************************************************
 */
/*---------------------------------------------------------------------------*/
extern Boolean lac_create_system(System_id system_id, Lac_system **system)
{
   Lac_port *p;
 
   if (!sysmalloc(sizeof(Lac_system), system)) return(False);
	/* else */
   (*system)->priority = Default_system_priority;
   (*system)->id       = system_id;

   p                 = &((*system)->ports);
   p->port_no        =     Zero;
   p->next           =     p;
   p->system         =   (*system);
   sys_null_timer(         &p->tick_timer);
   init_node(              &p->mux, (void *)p);
   init_node(              &p->mac, (void *)p);

   lac_init_system(*system);

	return(True);
}

/******************************************************************************
 * LAC : LINK AGGREGATION CONTROL PROTOCOL : CREATE AND ADD PORTS TO A SYSTEM
 ******************************************************************************
 */
/*---------------------------------------------------------------------------*/
static void add_port(Lac_system *system, Lac_port *port)
{
   Lac_port *p;
   Lac_port *p0 = &system->ports;

   port->next = p0;

   p = p0;
   while (p->next != p0) p = p->next;
   
   p->next         = port;
   port->system    = system;

   sys_create_timer(&port->tick_timer,   lac_tick, port);
   sys_create_timer(&port->tx_scheduler, tx_opportunity, port);
}

/*---------------------------------------------------------------------------*/
extern Boolean lac_create_port(Lac_system *system, Port_no port_no,
                               Lac_port  **port)
{
	Lac_port *p;

   *port = NULL;
   p = &system->ports;
   while ((p = p->next) != &system->ports)
      if (p->port_no == port_no) return(False);

	/* else */
	if (!sysmalloc(sizeof(Lac_port), &p)) return(False);

   *port = p;
   p->port_no = port_no;

   init_node(&p->mac, (void *)p);
   p->mac.rx_fn        = &mac_rx;
   p->mac.rx_status_fn = &mac_status;
   p->mac.tx_status_fn = &mac_status;

   init_node(&p->mux, (void *)p);

   add_port(system, p);

   p->actor_admin.port_priority         = Default_port_priority;
   p->actor_admin.port_no               = port_no;
   p->actor_admin.system_priority       = Default_system_priority;
   p->actor_admin.system_id             = p->system->id;
   p->actor_admin.key                   = Default_key;
   p->actor_admin.state.lacp_activity   = Default_lacp_activity;
   p->actor_admin.state.lacp_timeout    = Default_lacp_timeout;
   p->actor_admin.state.aggregation     = Default_aggregation;
   p->actor_admin.state.synchronization = False;
   p->actor_admin.state.defaulted       = True;
   p->actor_admin.state.expired         = False;

   p->partner_admin.port_priority         = Default_port_priority;
   p->partner_admin.port_no               = port_no;
   p->partner_admin.system_priority       = Default_system_priority;
   p->partner_admin.system_id             = Null_system;
   p->partner_admin.key                   = p->port_no;
   p->partner_admin.state.lacp_activity   = False; /* Passive      */
   p->partner_admin.state.lacp_timeout    = False; /* Long timeout */
   p->partner_admin.state.aggregation     = False; /* Individual   */
   p->partner_admin.state.synchronization = True;
   p->partner_admin.state.collecting      = True;
   p->partner_admin.state.distributing    = True;
   p->partner_admin.state.defaulted       = True;
   p->partner_admin.state.expired         = False;

	lac_init_port(system, port_no, Lacp_enabled);

   return(True);
}

/******************************************************************************
 * LAC : LINK AGGREGATION CONTROL PROTOCOL : REMOVE AND DESTROY PORTS, SYSTEM
 ******************************************************************************
 */
/*---------------------------------------------------------------------------*/
extern void lac_destroy_port(Lac_system *system, Port_no port_no);
  /*
	* Removes port number "port_no" from "system", releasing associated
	* resources.
	*/

/*---------------------------------------------------------------------------*/
extern void lac_destroy_system(Lac_system *system);
  /*
	* Destroys "system" and any remaining system ports.
	*/
  
