/*
 * 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.
 * 
 */

/*
 *
 * Client registration/query funcs used in Digital's code
 *
 * $Id: address.c,v 1.13 1996/08/06 14:14:11 carnil Exp carnil $
 *
 */


/* Global prototypes */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>

/* Atm includes */
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <sys/ioctl.h>
#include <atm.h>

/* Digital prototypes */
#include "g_types.h"
#include "atm.h"
#include "af_lane.h"
#include "addr_reg.h"
#include "codes.h"
#include "g_event.h"
#include "utl_os.h"
#include "le_disp.h"

#define EMOD MOD_ADDR_REG
#define EINST "address.c"

#include "emask.h"

#define ADDR_TIMER_FREQ 5000

/* One-way list. There won't be that many clients registered.. */
typedef struct _client_t_ {
  ADDR_REG_CALLBACK callback; /* Callback func */
  HANDLE callback_handle; /* Handle to give in callback */
  char *name; /* Describing text string */
  ADDR_ATM addr; /* ATM address for this client */  
  struct _client_t_ *next; 
  HANDLE my_timer; /* See that ATM address is maintained */
  int notify;
} Client_t;

Client_t *Clientlist = NULL;

extern int errno;

void addr_reg_timer_callback(HANDLE context);

STATUS 
addr_reg_client_register (HANDLE addr_reg_handle, ADDR_REG_CALLBACK callback,
			  HANDLE callback_handle, const char *p_text,
			  HANDLE *p_client_handle)
{
  Client_t *to_register;

  EVENT(EM_DEBUG,("Addr_reg_client_register\n"));
  assert(callback);
  to_register = (Client_t*)mem_alloc(EINST,sizeof(Client_t));
  if (!to_register)
    return STATUS_K_RESOURCES;
  to_register->callback = callback;
  to_register->callback_handle = callback_handle;
  if (p_text) {
    to_register->name = (char*)mem_alloc(EINST,
					 sizeof(char)*(strlen(p_text)+1));
    if (!to_register->name) {
      mem_free(EINST,to_register);
      return STATUS_K_RESOURCES;
    }
    memcpy(to_register->name, p_text, strlen(p_text));
    to_register->name[strlen(p_text)] = '\0';
  } else {
    /* No name. Still allocate some room, to avoid suprises in
       memory references to name :) */
    to_register->name = (char*)mem_alloc(EINST,sizeof(char));
    if (!to_register->name) {
      mem_free(EINST,to_register);
      return STATUS_K_RESOURCES;
    }
    to_register->name[0] = '\0';
  }
  to_register->notify=0;
  *p_client_handle = to_register;
  memset(&to_register->addr,0,sizeof(ADDR_ATM));
  to_register->next = Clientlist;
  Clientlist = to_register;
  return os_timer_alloc(addr_reg_timer_callback, to_register, 
			&to_register->my_timer);
}

int 
addr_reg_client_deregister(HANDLE client_handle)
{
  Client_t *client = client_handle;

  if (!client) {
    EVENT(EM_NERR,("Address registration deregistering nonexisting client\n"));
    return -1;
  }
  os_timer_dealloc(client->my_timer);
  mem_free(EINST, client->name);
  mem_free(EINST, client);
  return 0;
}

STATUS 
addr_reg_atm_addr_alloc(HANDLE client_handle)
{
  Client_t *client;

  EVENT(EM_DEBUG,("Addr_reg_atm_addr_alloc\n"));

  client = (Client_t *)client_handle;

  if (ATM_EQUAL_NULL(client->addr)) {
    client->notify=1;
    addr_reg_timer_callback((HANDLE)client);
  } else {
    client->notify=0;
    client->callback(client->callback_handle,&client->addr, 
		     ADDR_REG_EVENT_ATM_ADDR_ALLOCATED);
    addr_reg_timer_callback((HANDLE)client);
  }
  return STATUS_K_SUCCESS;
}

STATUS 
addr_reg_atm_addr_dealloc (HANDLE client_handle, ADDR_ATM *p_atm_addr)
{
  Client_t *client;

  EVENT(EM_DEBUG,("Addr_reg_atm_addr_dealloc\n"));

  client = (Client_t *)client_handle;

  /* Dealloc atm address, if allocated */
  ATM_COPY_NULL(client->addr);
  /* Call callback */
  return STATUS_K_SUCCESS;
}

void
addr_set_atm_addr(ADDR_ATM *set_to)
{
  Client_t *client = Clientlist;

  ATM_COPY(*set_to, client->addr);
  EVENT(EM_DEBUG,("Addr_set_atm_addr %s\n",disp_atm_text(client->addr)));
}

int
address_convert(char* parsestring, ADDR_ATM *atm_addr)
{
  struct sockaddr_atmsvc tmp;

  if (text2atm(parsestring, (struct sockaddr*)&tmp, sizeof(tmp),
	       T2A_NAME) <0) {
    return -1;
  }
  atm_addr->prefix[0] = tmp.sas_addr.prv[0];
  atm_addr->prefix[1] = tmp.sas_addr.prv[1];
  atm_addr->prefix[2] = tmp.sas_addr.prv[2];
  atm_addr->prefix[3] = tmp.sas_addr.prv[3];
  atm_addr->prefix[4] = tmp.sas_addr.prv[4];
  atm_addr->prefix[5] = tmp.sas_addr.prv[5];
  atm_addr->prefix[6] = tmp.sas_addr.prv[6];
  atm_addr->prefix[7] = tmp.sas_addr.prv[7];
  atm_addr->prefix[8] = tmp.sas_addr.prv[8];
  atm_addr->prefix[9] = tmp.sas_addr.prv[9];
  atm_addr->prefix[10] = tmp.sas_addr.prv[10];
  atm_addr->prefix[11] = tmp.sas_addr.prv[11];
  atm_addr->prefix[12] = tmp.sas_addr.prv[12];
  atm_addr->esi[0] = tmp.sas_addr.prv[13];
  atm_addr->esi[1] = tmp.sas_addr.prv[14];
  atm_addr->esi[2] = tmp.sas_addr.prv[15];
  atm_addr->esi[3] = tmp.sas_addr.prv[16];
  atm_addr->esi[4] = tmp.sas_addr.prv[17];
  atm_addr->esi[5] = tmp.sas_addr.prv[18];
  atm_addr->sel = tmp.sas_addr.prv[19];
  return 0;
}

void 
addr_reg_timer_callback(HANDLE context)
{
  Client_t *client = (Client_t *)context;
  unsigned char *buffer;
  struct sockaddr_atmsvc *ouraddr;
  struct atmif_sioc req;
  int s,i;
  static int address_invalidated =0; /* If address is missing twice,
					then act */

  EVENT(EM_DEBUG,("Addr_reg_timer_called\n"));
  buffer = (unsigned char*)mem_alloc(EINST, 5*sizeof(struct sockaddr_atmsvc));
  req.number = 0;
  req.arg = buffer;
  req.length = 5*sizeof(*ouraddr);

  /* Reset (or set) timer */
  os_timer_set(client->my_timer, ADDR_TIMER_FREQ+1);

  s = socket(PF_ATMSVC, SOCK_DGRAM, 0);
  if (s<0) {
    EVENT(EM_NERR,("Socket failed:%s\n",strerror(errno)));
    mem_free(EINST, buffer);
    return;
  }
  if (ioctl(s, ATM_GETADDR, &req) <0) {
    close(s);
    EVENT(EM_NERR,("Ioctl failed:%s\n",strerror(errno)));
    mem_free(EINST, buffer);
    return;
  }
  close(s);

  if (req.length ==0 ) { /* No ATM address */
    if (ATM_EQUAL_NULL(client->addr)) {/* Address hasn't been registered 
				        * and wasn't registered now */
      mem_free(EINST, buffer);
      return;
    }
    if (address_invalidated) { /* Twice is enough */
      EVENT(EM_NERR,("Address invalidated\n"));
      client->callback(client->callback_handle, &client->addr,
		       ADDR_REG_EVENT_ATM_ADDR_INVALIDATED);
      ATM_COPY_NULL(client->addr);
      mem_free(EINST, buffer);
      return;
    } else {
      address_invalidated = 1;
      mem_free(EINST, buffer);
      return;
    }
  }
  address_invalidated = 0;
  EVENT(EM_DEBUG,("We have address\n"));
  if (!client->notify && !ATM_EQUAL_NULL(client->addr)){  /* Address has already been registered*/
    EVENT(EM_DEBUG,("Old %s\n",disp_atm_text(client->addr)));
    for(i=0;i < req.length/sizeof(struct sockaddr_atmsvc); i++) {
      /* Possibility for several ATM addresses */   
      ouraddr = (struct sockaddr_atmsvc*)buffer+i;
      EVENT(EM_DEBUG,("Tested agaist %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
		      ouraddr->sas_addr.prv[0],ouraddr->sas_addr.prv[1],
		      ouraddr->sas_addr.prv[2],ouraddr->sas_addr.prv[3],
		      ouraddr->sas_addr.prv[4],ouraddr->sas_addr.prv[5],
		      ouraddr->sas_addr.prv[6],ouraddr->sas_addr.prv[7],
		      ouraddr->sas_addr.prv[8],ouraddr->sas_addr.prv[9],
		      ouraddr->sas_addr.prv[10],ouraddr->sas_addr.prv[11],
		      ouraddr->sas_addr.prv[12],ouraddr->sas_addr.prv[13],
		      ouraddr->sas_addr.prv[14],ouraddr->sas_addr.prv[15],
		      ouraddr->sas_addr.prv[16],ouraddr->sas_addr.prv[17],
		      ouraddr->sas_addr.prv[18],ouraddr->sas_addr.prv[19]));
      if (client->addr.prefix[0] == ouraddr->sas_addr.prv[0] &&
	  client->addr.prefix[1] == ouraddr->sas_addr.prv[1] &&
	  client->addr.prefix[2] == ouraddr->sas_addr.prv[2] &&
	  client->addr.prefix[3] == ouraddr->sas_addr.prv[3] &&
	  client->addr.prefix[4] == ouraddr->sas_addr.prv[4] &&
	  client->addr.prefix[5] == ouraddr->sas_addr.prv[5] &&
	  client->addr.prefix[6] == ouraddr->sas_addr.prv[6] &&
	  client->addr.prefix[7] == ouraddr->sas_addr.prv[7] &&
	  client->addr.prefix[8] == ouraddr->sas_addr.prv[8] &&
	  client->addr.prefix[9] == ouraddr->sas_addr.prv[9] &&
	  client->addr.prefix[10] == ouraddr->sas_addr.prv[10] &&
	  client->addr.prefix[11] == ouraddr->sas_addr.prv[11] &&
	  client->addr.prefix[12] == ouraddr->sas_addr.prv[12] &&
	  client->addr.esi[0] == ouraddr->sas_addr.prv[13] &&
	  client->addr.esi[1] == ouraddr->sas_addr.prv[14] &&
	  client->addr.esi[2] == ouraddr->sas_addr.prv[15] &&
	  client->addr.esi[3] == ouraddr->sas_addr.prv[16] &&
	  client->addr.esi[4] == ouraddr->sas_addr.prv[17] &&
	  client->addr.esi[5] == ouraddr->sas_addr.prv[18]) {
	/*...and has stayed same*/
	mem_free(EINST, buffer);
	return;
      }
    }
    EVENT(EM_DEBUG,("... which is new one\n"));
    /* Hasn't stayed same. First invalidate previous, then register new. */
    client->callback(client->callback_handle, &client->addr,
		     ADDR_REG_EVENT_ATM_ADDR_INVALIDATED);
  }
  EVENT(EM_DEBUG,("Register new one\n"));
  client->notify=0;
  ouraddr = (struct sockaddr_atmsvc *)buffer;
  client->addr.prefix[0] = ouraddr->sas_addr.prv[0];
  client->addr.prefix[1] = ouraddr->sas_addr.prv[1];
  client->addr.prefix[2] = ouraddr->sas_addr.prv[2];
  client->addr.prefix[3] = ouraddr->sas_addr.prv[3];
  client->addr.prefix[4] = ouraddr->sas_addr.prv[4];
  client->addr.prefix[5] = ouraddr->sas_addr.prv[5];
  client->addr.prefix[6] = ouraddr->sas_addr.prv[6];
  client->addr.prefix[7] = ouraddr->sas_addr.prv[7];
  client->addr.prefix[8] = ouraddr->sas_addr.prv[8];
  client->addr.prefix[9] = ouraddr->sas_addr.prv[9];
  client->addr.prefix[10] = ouraddr->sas_addr.prv[10];
  client->addr.prefix[11] = ouraddr->sas_addr.prv[11];
  client->addr.prefix[12] = ouraddr->sas_addr.prv[12];
  client->addr.esi[0] = ouraddr->sas_addr.prv[13];
  client->addr.esi[1] = ouraddr->sas_addr.prv[14];
  client->addr.esi[2] = ouraddr->sas_addr.prv[15];
  client->addr.esi[3] = ouraddr->sas_addr.prv[16];
  client->addr.esi[4] = ouraddr->sas_addr.prv[17];
  client->addr.esi[5] = ouraddr->sas_addr.prv[18];
  client->addr.sel = ouraddr->sas_addr.prv[19];
  client->callback(client->callback_handle,&client->addr, 
		   ADDR_REG_EVENT_ATM_ADDR_ALLOCATED);
  mem_free(EINST, buffer);
}

int 
addr_getesi(unsigned char *mac_addr)
{
  int fd;
  struct atmif_sioc req;

  fd=socket(PF_ATMSVC, SOCK_DGRAM, 0);
  if (!fd) {
    EVENT(EM_DEBUG,("Failed to create ATM socket:%s\n",strerror(errno)));
    return -1;
  }
  req.number=0;
  req.arg=mac_addr;
  req.length=ESI_LEN;
  if (ioctl(fd, ATM_GETESI, &req)<0) {
    EVENT(EM_DEBUG,("ioctl failed:%s\n",strerror(errno)));
    return -1;
  }
  close(fd);
  return 0;
}

int
addr_getouratmaddr(struct sockaddr_atmsvc *addr)
{
  Client_t *client;

  client = Clientlist;

  if (!client) {
    EVENT(EM_SERR,("Addr_getouratmaddr, no ATM address!!!\n"));
    return -1;
  }
  if (ATM_EQUAL_NULL(client->addr)) {
    addr_reg_timer_callback(client);
  }
  if (ATM_EQUAL_NULL(client->addr)) {
    EVENT(EM_SERR,("Addr_getouratmaddr, no ATM address!!!\n"));
    return -1;
  }
  memset(addr,0,sizeof(*addr));
  addr->sas_family = AF_ATMSVC;
  addr->sas_addr.prv[0] = client->addr.prefix[0];
  addr->sas_addr.prv[1] = client->addr.prefix[1];
  addr->sas_addr.prv[2] = client->addr.prefix[2];
  addr->sas_addr.prv[3] = client->addr.prefix[3];
  addr->sas_addr.prv[4] = client->addr.prefix[4];
  addr->sas_addr.prv[5] = client->addr.prefix[5];
  addr->sas_addr.prv[6] = client->addr.prefix[6];
  addr->sas_addr.prv[7] = client->addr.prefix[7];
  addr->sas_addr.prv[8] = client->addr.prefix[8];
  addr->sas_addr.prv[9] = client->addr.prefix[9];
  addr->sas_addr.prv[10] = client->addr.prefix[10];
  addr->sas_addr.prv[11] = client->addr.prefix[11];
  addr->sas_addr.prv[12] = client->addr.prefix[12];
  addr->sas_addr.prv[13] = client->addr.esi[0];
  addr->sas_addr.prv[14] = client->addr.esi[1];
  addr->sas_addr.prv[15] = client->addr.esi[2];
  addr->sas_addr.prv[16] = client->addr.esi[3];
  addr->sas_addr.prv[17] = client->addr.esi[4];
  addr->sas_addr.prv[18] = client->addr.esi[5];
  addr->sas_addr.prv[19] = client->addr.sel;
  return 0;
}


/*
 *
 * $Log: address.c,v $
 * Revision 1.13  1996/08/06 14:14:11  carnil
 * Address handling moved to address.c from conn.c
 *
 * Revision 1.12  1996/07/07 11:51:47  carnil
 * Address changes are noticed and notified
 *
 * Revision 1.11  1996/04/25 19:42:13  carnil
 * Copyright notice
 *
 * Revision 1.10  1996/04/19 06:37:49  carnil
 * addr_getesi
 *
 * Revision 1.9  1996/04/11 09:16:14  carnil
 * address copy bug fix
 *
 * Revision 1.8  1996/03/17 21:23:41  carnil
 * *** empty log message ***
 *
 * Revision 1.7  1996/02/29 11:24:12  carnil
 * address allocation with ioctl
 *
 * Revision 1.6  1996/02/19 16:32:01  carnil
 * Address ioctl
 *
 * Revision 1.5  1996/02/16 06:16:20  carnil
 * oshandle,cmhandle removed
 *
 * Revision 1.4  1996/01/29  09:30:43  carnil
 * mem_alloc, mem_free
 *
 * Revision 1.3  1996/01/23  10:02:12  carnil
 * Debug info added
 * addr_reg_atm_addr_dealloc
 *
 * Revision 1.2  1996/01/22  12:59:50  carnil
 * Proto diff fixed
 *
 * Revision 1.1  1996/01/19  13:24:31  carnil
 * Initial revision
 *
 *
 */
