/*
 * Marko Kiiskila carnil@cs.tut.fi 
 * 
 * Copyright (c) 1996
 * Tampere University of Technology - Telecommunications Laboratory
 * All rights reserved.
 *
 * Permission to use, copy, modify and distribute this
 * software and its documentation is hereby granted,
 * provided that both the copyright notice and this
 * permission notice appear in all copies of the software,
 * derivative works or modified versions, and any portions
 * thereof, that both notices appear in supporting
 * documentation, and that the use of this software is
 * acknowledged in any publications resulting from using
 * the software.
 * 
 * TUT ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
 * SOFTWARE.
 * 
 */
/*
 * Lec arp cache daemon part
 *
 * $Id: lec_arp.c,v 1.13 1996/08/06 14:14:11 carnil Exp carnil $
 *
 */

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <linux/atmlec.h>

/* Include General Headers. */

#include "g_types.h"    /* General Purpose Types      */
#include "codes.h"      /* Return Status Codes        */
#include "atm.h"        /* ATM Specific Definitions   */
#include "utl.h"        /* General Purpose Utilities  */
#include "utl_os.h"     /* Operating System Utilities */
#include "system.h"
#include "g_event.h"

#include "af_lane.h"
#include "cm.h"
#include "svc_info.h"
#include "lec.h"
#include "lec_arp.h"

#include "kernel_itf.h"
#include "conn.h"

#define EMOD   MOD_LEC_ARP
#define EINST  "lec_arp.c"
#include "emask.h"

typedef struct _elan {
  struct _elan  *p_next;
  char *p_text;
  HANDLE la_handle;
  HANDLE lc_elan_handle;
  HANDLE lport_handle;
  BOOLEAN configured;
} LA_ELAN_CONTEXT;

SIMPLE_LIST (LA_ELAN_CONTEXT, LA_ELAN_LIST);

typedef struct {
  HANDLE               lc_handle;
  HANDLE               vc_list;
  HANDLE               atm_list;
  ARP_XMT_CALLBACK     arp_xmt_callback;
  FLUSH_XMT_CALLBACK   flush_xmt_callback;
  SVC_SETUP_CALLBACK   svc_setup_callback;
  LA_ELAN_LIST         elan_list;
} LA_CONTEXT;

static LA_CONTEXT lec_arp_context;

static void
kernel_arp_xmt_callback(struct atmlec_msg *mesg)
{
  LA_ELAN_CONTEXT *p_elan;
  ESI *to_pass;

  to_pass = (ESI*)mesg->content.normal.mac_addr;

  utl_list_traverse(lec_arp_context.elan_list, p_elan) {
    lec_arp_context.arp_xmt_callback(p_elan->lc_elan_handle, *to_pass);
  }
}

void
kernel_flush_xmt(unsigned char *to_pass)
{
  LA_ELAN_CONTEXT *p_elan;
  UINT32 dumb;

  utl_list_traverse(lec_arp_context.elan_list, p_elan) {
    lec_arp_context.flush_xmt_callback(p_elan->lc_elan_handle, to_pass,&dumb);
    EVENT(EM_DEBUG,("Setting flush transaction id to %lx\n",dumb));
    kernel_sendmsg(l_flush_tran_id, NULL, to_pass,
		   NULL, dumb);
  }
}

static void
kernel_svc_setup_xmt_callback(struct atmlec_msg *mesg)
{
  LA_ELAN_CONTEXT *p_elan;
  ADDR_ATM *to_pass;

  to_pass = (ADDR_ATM*)mesg->content.normal.atm_addr;

  utl_list_traverse(lec_arp_context.elan_list, p_elan) {
    lec_arp_context.svc_setup_callback(p_elan->lc_elan_handle, *to_pass);
  }
}

STATUS 
la_create (HANDLE lc_handle, ARP_XMT_CALLBACK arp_xmt_callback,
	   FLUSH_XMT_CALLBACK flush_xmt_callback,
	   SVC_SETUP_CALLBACK svc_setup_callback,
	   HANDLE *p_la_handle)
{
  lec_arp_context.lc_handle = lc_handle;
  lec_arp_context.arp_xmt_callback    = arp_xmt_callback;
  lec_arp_context.flush_xmt_callback  = flush_xmt_callback;
  lec_arp_context.svc_setup_callback  = svc_setup_callback;
  
  utl_list_init(lec_arp_context.elan_list);

  kernel_register_callback(l_arp_xmt, kernel_arp_xmt_callback);
  kernel_register_callback(l_svc_setup, kernel_svc_setup_xmt_callback);

  *p_la_handle = (HANDLE)&lec_arp_context;

  return STATUS_K_SUCCESS;
}

void 
la_destroy(HANDLE la_handle)
{
  LA_CONTEXT *context;
  LA_ELAN_CONTEXT *elan;

  context = (LA_CONTEXT *)la_handle;
  utl_list_traverse (context->elan_list, elan) {
    la_deregister((HANDLE)elan);
  }
}

STATUS 
la_register(HANDLE la_handle, HANDLE lc_elan_handle,
	    HANDLE lport_handle, char *p_text,
	    HANDLE *p_la_elan_handle)
{
  LA_CONTEXT        *p_context;
  LA_ELAN_CONTEXT   *p_elan;
  
  /* Obtain a pointer to the LA Context block. */
  
  p_context = (LA_CONTEXT *) la_handle;

  /* Allocate space for the LA_ELAN context block.  If there is a problem,
   * return error status.
   */
  p_elan = (LA_ELAN_CONTEXT *) mem_alloc(EINST, sizeof(LA_ELAN_CONTEXT));
  if (p_elan == NULL)
    return STATUS_K_RESOURCES;
  
  /* Initialize the fields of the LA_ELAN context block.  Mark the ELAN as
   * unconfigured.
   */
  p_elan->la_handle      = la_handle;
  p_elan->lc_elan_handle = lc_elan_handle;
  p_elan->lport_handle   = lport_handle;
  p_elan->configured     = FALSE;
  p_elan->p_text         = p_text;

  utl_list_add (p_context->elan_list, p_elan);
  
  *p_la_elan_handle = (HANDLE) p_elan;
  return STATUS_K_SUCCESS;
}

void 
la_deregister(HANDLE la_elan_handle)
{
  LA_CONTEXT        *p_context;
  LA_ELAN_CONTEXT   *p_elan;

  p_elan    = (LA_ELAN_CONTEXT *) la_elan_handle;
  p_context = (LA_CONTEXT      *) p_elan->la_handle;
  
  utl_list_delete(p_context->elan_list, p_elan);
  
  mem_free(EINST,p_elan);
}

void 
la_config(HANDLE la_elan_handle, LAN_TYPE lan_type,
	  BOOLEAN proxy_flag, UINT16 max_unknown_frame_count,
	  UINT16 max_unknown_frame_time, UINT32 vcc_timeout,
	  UINT16 max_retry_count, UINT32 aging_time,
	  UINT16 forward_delay_time, UINT16 le_arp_response_time,
	  UINT16 flush_timeout, UINT16 path_switching_delay)
{
  LA_ELAN_CONTEXT   *p_elan;
  struct atmlec_config_msg conf_mesg;

  p_elan = (LA_ELAN_CONTEXT *) la_elan_handle;

  p_elan->configured               = TRUE;
  conf_mesg.maximum_unknown_frame_count = max_unknown_frame_count;
  conf_mesg.max_unknown_frame_time = max_unknown_frame_time;
  conf_mesg.max_retry_count = max_retry_count;
  conf_mesg.aging_time = aging_time;
  conf_mesg.forward_delay_time = forward_delay_time;
  conf_mesg.arp_response_time = le_arp_response_time;
  conf_mesg.flush_timeout = flush_timeout;
  conf_mesg.path_switching_delay = path_switching_delay;
  kernel_sendmsg(l_config, NULL, NULL, &conf_mesg, 0);
}

STATUS 
la_addr_delete(HANDLE la_elan_handle,
	       unsigned char *atm_addr,
	       BOOLEAN permanent)
{
  kernel_sendmsg(l_addr_delete, NULL, atm_addr, NULL,
		  permanent==TRUE?1:0);
  return STATUS_K_SUCCESS;
}

void 
la_arp_update(HANDLE    la_elan_handle,
	      ESI       esi,
	      ADDR_ATM  addr_atm,
	      BOOLEAN   remote_flag)
{
  EVENT(EM_DEBUG,("ARP update for %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
		  esi[0],esi[1],esi[2],esi[3],esi[4],esi[5]));
  kernel_sendmsg(l_arp_update, (unsigned char *)esi,
		 (unsigned char *)&addr_atm, NULL, remote_flag==TRUE?1:0);
}

void 
la_flush_complete(HANDLE la_elan_handle,
		  UINT32   tran_id)
{
  EVENT(EM_DEBUG,("Flush complete for tran_id %lx\n", tran_id));
  kernel_sendmsg(l_flush_complete, NULL,
		 NULL, NULL, tran_id);
}

void 
la_topology_change_set(HANDLE  la_elan_handle,
		       BOOLEAN topology_change_flag)
{
  kernel_sendmsg(l_topology_change, NULL, NULL, NULL,
		 topology_change_flag==TRUE?1:0);
}


/*
 *
 * $Log: lec_arp.c,v $
 * Revision 1.13  1996/08/06 14:14:11  carnil
 * Cleaning up
 *
 * Revision 1.12  1996/07/07 11:51:47  carnil
 * Global msg_mask
 * Arp cache deletion by atm_address
 *
 * Revision 1.11  1996/06/10 04:27:28  carnil
 * Unused funcs removed, compiler warning fix
 *
 * Revision 1.10  1996/04/25 19:42:13  carnil
 * Copyright notice
 *
 * Revision 1.9  1996/04/20 16:24:47  carnil
 * conn_convert removed
 *
 * Revision 1.8  1996/04/19 06:37:49  carnil
 * Kernel mesg bug fixes
 *
 * Revision 1.7  1996/03/18 16:43:50  carnil
 * Bug fixes
 *
 * Revision 1.6  1996/03/17 21:23:41  carnil
 * callbacks from kernel, transaction id
 *
 * Revision 1.5  1996/03/15 07:08:09  carnil
 * Converted to use kernel table
 *
 *
 */
