/*	sys.c   2.00.000 01JUN99 01:23  */

#include "sys.h"
/******************************************************************************
 * SYS : SYSTEM SUPPLIED MEMORY ALLOCATION ROUTINES
 ******************************************************************************
 */
Boolean sysmalloc(int size, void **allocated)
{
   *allocated = malloc(size);

   if (*allocated != NULL) return(True);
   else                    return(False);
}

void sysfree(void *allocated)
{
   free(allocated);
}

/******************************************************************************
 * SYSTIME : SYSTEM SUPPLIED SCHEDULING FUNCTIONS
 ******************************************************************************
 */
static Time   now;
static Timer  first_timer;
static Timer *tick_next;

/*---------------------------------------------------------------------------*/
void sys_init_timers()
{
   /* initialize timer support */
   now = (Time)1;

   first_timer.expiry_at = (Time)0;
   first_timer.expiry_fn  = NULL;
   first_timer.expiry_arg = &first_timer;

   first_timer.next_timer = NULL;

   tick_next = first_timer.next_timer;
}

/*---------------------------------------------------------------------------*/
void sys_create_timer(Timer *timer, void (*call_fn)(void *), void *with_arg)
{
   Timer *in_chain;

   timer->expiry_at  = (Time)0;
   timer->expiry_fn   = call_fn;
   timer->expiry_arg  = with_arg;
   timer->next_timer = NULL;

   in_chain = &first_timer;
   while (in_chain->next_timer != NULL)
      in_chain = in_chain->next_timer;

   in_chain->next_timer = timer;
}

/*---------------------------------------------------------------------------*/
void sys_null_timer(Timer *timer)
{/*
  * Tidy way to initialise Timer structs included in other data structures
  * when the timer is not required. Do not attempt to start, stop, or otherwise
  * operate on this 'timer'.
  */
   timer->expiry_at   = (Time)0;
   timer->expiry_fn   = (void (*)(void *))NULL;
   timer->expiry_arg  = NULL;
   timer->next_timer  = NULL;
}

/*---------------------------------------------------------------------------*/
void sys_destroy_timer(Timer *timer)
{
   Timer *in_chain;
   
   in_chain = &first_timer;
   while (in_chain->next_timer != timer)
   {  assert(in_chain->next_timer != NULL);
      in_chain = in_chain->next_timer;
   }

   in_chain->next_timer = in_chain->next_timer->next_timer;
}

/*---------------------------------------------------------------------------*/
void sys_start_timer(Timer *timer, Time to_run)
{
   Time expiry;

   assert(timer->expiry_at == (Time)0);

          expiry    =  now + to_run;
   if    (expiry    == (Time) (0)) ++expiry;
   timer->expiry_at =  expiry;
}

/*---------------------------------------------------------------------------*/
void sys_stop_timer(Timer *timer)
{
  assert(timer->expiry_at != (Time)0);
  
  timer->expiry_at  = (Time)0;
}

/*---------------------------------------------------------------------------*/
void sys_cstop_timer(Timer *timer)
{
  timer->expiry_at  = (Time)0;
}

/*---------------------------------------------------------------------------*/
void sys_tick()
{
   if (++now == (Time)0) ++now;

   tick_next = first_timer.next_timer;

   while (tick_next != NULL)
   {
      if (tick_next->expiry_at == now)
      {
         tick_next->expiry_at = (Time)0;
         tick_next->expiry_fn(tick_next->expiry_arg);
      }
      tick_next = tick_next->next_timer;
   }
   tick_next = first_timer.next_timer;
}

/*---------------------------------------------------------------------------*/
Boolean sys_check_expiry(Ticks *user_timing_down_counter)
{
   if (*user_timing_down_counter != Stopped)
   {   *user_timing_down_counter -=1;
       if (*user_timing_down_counter == Expired) return(True);
   }
   return(False);
}

/*---------------------------------------------------------------------------*/
int sys_time()
{
   return(now - 1);
}

/******************************************************************************
 * SYSINIT : INITIALIZE SYSTEM SUPPORT
 ******************************************************************************
 */
/*---------------------------------------------------------------------------*/
void sys_init()
{
   sys_init_timers();
}

/******************************************************************************
 * SYSNODE : PROTOCOL MODULE INTERCONNECTION
 ******************************************************************************
 */
/*---------------------------------------------------------------------------*/
extern void init_node(Node *node, void *owner)
{
   node->next         =   node;
   node->owner        =   owner;
   node->pnext        =   node;
   node->provider     =   NULL;
   node->user         =   NULL;

   node->node_id      =  (Node_id)0;

   node->rx_fn        = &(void (*)(Node *, void *)) syserr_panic;
   node->tx_fn        = &(void (*)(Node *, void *)) syserr_panic;

   node->rx_status_fn = &(void (*)(Node *, Boolean, void *)) syserr_panic;
   node->tx_status_fn = &(void (*)(Node *, Boolean, void *)) syserr_panic;

   node->rx_on        =   False;
   node->tx_on        =   False;

   node->rx_enabled   =   False;
   node->tx_enabled   =   False;
}

/*---------------------------------------------------------------------------*/
extern Boolean isolated_node(Node *node)
{
   return(node->next == node);
}

/*---------------------------------------------------------------------------*/
extern void sys_attach_node(Node *ring, Node *node)
{
   Node *n;

   node->next = ring;

   n = ring;
   while (n->next != ring)
          n = n->next;

   n->next  = node;
}

/*---------------------------------------------------------------------------*/
extern void sys_rx(Node *node, void *pdu)
{
    if (node->rx_enabled)
       node->rx_fn(node, pdu);
}

/*---------------------------------------------------------------------------*/
extern void sys_node_rx(Node *node, void *pdu)
{
   if ((node->rx_enabled) && (node->user != NULL))
      sys_rx(node->user, pdu);
}

/*---------------------------------------------------------------------------*/
extern Boolean sys_tx(Node *node, void *pdu)
{
    if (node->tx_enabled)
    {
       node->tx_fn(node, pdu);
       return(True);
    }
    else return(False);
}

/*---------------------------------------------------------------------------*/
extern void sys_node_tx(Node *node, void *pdu)
{
   if ((node->tx_enabled) && (node->provider != NULL))
      (void)sys_tx(node->provider, pdu);
}

/*---------------------------------------------------------------------------*/
extern void sys_node_rx_status(Node *node, Boolean yes, void *arg)
{
   if (node->user != NULL)
      node->user->rx_status_fn(node->user, yes, arg);
}

/*---------------------------------------------------------------------------*/
extern void sys_node_tx_status(Node *node, Boolean yes, void *arg)
{
   if (node->user != NULL)
      node->user->tx_status_fn(node->user, yes, arg);
}

/******************************************************************************
 * SYSLAN : LAN ATTACHMENT, TRANSMISSION AND RECEPTION.
 ******************************************************************************
 */
/*---------------------------------------------------------------------------*/
extern Boolean sys_create_lan(Lan **lan)
{
   if (!sysmalloc(sizeof(Lan), lan)) return(False);

   init_node((*lan),  (*lan));
   (*lan)->provider = (*lan);
}

/*---------------------------------------------------------------------------*/
extern void sys_attach_lan_node(Node *lan, Node *mac)
{
   Node *m;

   mac->provider = lan;
   mac->pnext    = lan;

   m = lan; while (m->pnext != lan) m = m->pnext;
   m->pnext      = mac;

   mac->rx_on        =   True;
   mac->tx_on        =   True;

   mac->tx_fn        =  &sys_lan_tx;

   mac->rx_enabled   =   True;
   mac->tx_enabled   =   True;

   mac->rx_status_fn(mac, True, NULL);
   mac->tx_status_fn(mac, True, NULL);
}
/*---------------------------------------------------------------------------*/
extern void sys_detach_lan_node(Node *mac)
{
   Node *m;

   m = mac->provider; while (m->pnext != mac) m = m->pnext;
   m->pnext   = mac->pnext;
   mac->pnext = mac;
   mac->provider = mac; // NULL


   mac->rx_on        =   False;
   mac->tx_on        =   False;

   mac->tx_fn        =  &sys_lan_tx;

   mac->rx_enabled   =   False;
   mac->tx_enabled   =   True; // False

   mac->rx_status_fn(mac, False, NULL);
   mac->tx_status_fn(mac, False, NULL);
}
/*---------------------------------------------------------------------------*/
extern void sys_lan_tx(Node *mac, void *pdu)
{
   Node *lan = mac->provider;
   Node *m;

   m = lan;
   while ((m = m->pnext) != lan)
   {  if (m == mac) continue;

      m->rx_fn(m, pdu);
}  }

/******************************************************************************
 * SYSERR : FATAL ERROR HANDLING
 ******************************************************************************
 */

syserr_panic()
{
   assert(False);
   return 1;
}
